1 为什么要迁移 Vue3.x
说点什么呢? 总之。。。 这不是我的错!
首先先要写个案例,找到一个可执行方案。
2 方案选择
参考Vue2转3官方文档
面对自己项目百八十个文件,人工爆肝肯定是不可能的,想都不能想! 唯一的方案是基于AST(抽象语法树)解构代码,根据Vue官网给出升级文档的修改建议,批量修改输出文件的方案。 只是。。。AST操作有点复杂。
调研了几个工具,GitHub上找到了几个去掉代码文件里 console.log 的示例学习学习:(这两处代码引用作对比方案,可以跳过) 一. 利用 jscodeshift 操作 AST去掉console.log 的示例:
export default (fileInfo, api) => {
const j = api.jscodeshift;
const root = j(fileInfo.source)
const callExpressions = root.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
object: { type: 'Identifier', name: 'console' },
},
}
);
callExpressions.remove();
return root.toSource();
};
复制代码
二. 利用 babel 操作 AST去掉console.log 的示例: (基于篇幅省略了部分代码,有兴趣的同学请访问github.com/mattphillip…)
export default function({
types,
}: typeof BabelCore): PluginObj<ConsoleTransformState> {
return {
name: 'console-transform',
visitor: {
CallExpression(path, { opts, file }) {
validateSchema(schema, opts);
const { env, removeMethods, additionalStyleMethods } = opts;
const callee = path.get('callee');
/*
基于篇幅限制
此处省略40+行代码
*/
},
},
};
}
复制代码
基于本人实力,这两个方案实在是劝退案例。
正在我为这个项目重构发愁,发量迅速减少,混迹于社区寻找解救方案的时候,忽然发现了GoGoCode这个工具。
贴一段官方介绍:
GoGoCode是一个操作AST的工具,可以降低使用AST的门槛,帮助开发者从繁琐的AST操作中解放出来,更专注于代码分析转换逻辑的开发。简单的替换甚至不用学习AST,而初步学习了AST节点结构(可参考AST查看器)后就可以完成更复杂的分析转换。
GoGoCode的官方文档 gogocode.io/zh/docs/spe…
这不正是我想要的么,遇到你就是古话说的,瞌睡来了有人递枕头! 而GoGoCode操作 AST 去掉 代码中console.log,仅仅只需要一行代码!!
$(`要转换的代码段`).find(`console.log($_$)`).remove()
复制代码
熟悉的 $ 符号,熟悉的find、remove等API,扣一下题,说零成本操作AST不算标题党吧 爆赞!!!方案选定!!!
3 开工
举个栗子:按键修饰符的迁移方案
Vue2转3官方文档 - 按键修饰符的迁移 v3.cn.vuejs.org/guide/migra…
以下是变更的简要总结:
- 非兼容:不再支持使用数字 (即键码) 作为v-on修饰符
- 非兼容:不再支持config.keyCodes
按照文档写一个待转化的Demo
<template>
<div>
<h1>迁移:按键修饰符</h1>
<p>迁移策略:
1.Vue3不再支持使用数字 (即键码) 作为 v-on 修饰符
2.不再支持 config.keyCodes</p>
<div class="mt20 text-left">
<div>space:<input type="text" @keyup.space="keys('space')" /></div>
<div>space:<input type="text" @keyup.32="keys('keycode 32 space')" /> </div>
<div>space:<input type="text" @keyup.customSpace="keys('keycode 32 space')" /> </div>
</div>
</div>
</template>
<script>
import Vue from 'vue';
Vue.config.keyCodes = {
customSpace: 32,
customDelete: 46
};
export default {
name: '按键修饰符',
methods: {
keys(key) {
alert('您按下的是' + key);
},
},
};
</script>
复制代码
任务确定
根据astexplorer.net分析下要转换的内容
安利一下astexplorer.net这个工具,我们可以利用它很方便的查看某段代码的AST语法树结构
这里要做的有三件事:
1)提取代码里自定义的keyCodes(上图蓝框Vue.config.keyCodes的内容部分),与系统keyCodes合并为一个map,后续要在template替换时使用;
2)移除Vue.config.keyCodes 代码(Vue3不再支持);
3)遍历所有标签以及它的属性,使用合并后的keyCodes替换标签(上图红框 xx.32与xx.customSpace部分)。
转换逻辑编写
- 项目里运行安装GoGoCode
npm install gogocode
复制代码
- 初始化script的AST对象
const $ = require('gogocode');
//script代码,用$转为AST节点
let scriptAst = $(`
import Vue from 'vue';
Vue.config.keyCodes = {
customSpace: 32,
customDelete: 46
};
export default {
name: '按键修饰符',
methods: {
keys(key) {
alert('您按下的是' + key);
},
},
};`)
复制代码
- 我们要寻找script里自定义的keyCodes(Vue.config.keyCodes的内容部分)
使用GoGoCode的find API,加上匹配通配符_,拿到所有的自定义keyCode数组
// 匹配取出自定义的keyCode,结果:Node数组
const customKeyCodeList = scriptAst.find(`Vue.config.keyCodes = {$_$}`).match[0]
复制代码
这里要注意构造匹配的 Vue.config.keyCodes = {$_$} 的字符串,需要符合可转AST节点的规范 可以拿到 astexplorer.net工具中验证一下
控制台打出customKeyCodeList,是一个Node数组
- customKeyCodeList加上系统的keyCode,构造全量的keyCodeMap
// 全量的keyCode对照表,基于篇幅这里只列出3个
// https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/keyCode
let keyCodeMap = {46: 'delete',32: 'space',112: 'f1'}
//加上自定义keyCode构造汇总所有的keyCodeMap,待会替换template内容的时候需要使用
//结果:{46: 'delete',32: 'space',112: 'f1', customSpace: 'space', customDelete: 'delete'}
for(let i = 0;i< customKeyCodeList.length; i=i+2){
Object.assign(keyCodeMap, {
[customKeyCodeList[i].value] : keyCodeMap[customKeyCodeList[i+1].value]
})
}
复制代码
控制台打出keyCodeMap构造结果
- Vue3要求: Vue.config.keyCodes不再支持,需要移除。find到这个节点,使用remove API移除
scriptAst.find(`Vue.config.keyCodes = $_$`).remove()
复制代码
- 初始化template节点,html模板需要带{ parseOptions: { html: true } }参数
let templateAst = $(`<template>
<div>
<p>迁移:按键修饰符</p>
<p>迁移策略:
1.Vue3不再支持使用数字 (即键码) 作为 v-on 修饰符
2.不再支持 config.keyCodes</p>
<div class="mt20 text-left">
<div>space:<input type="text" @keyup.space="keys('space')" /></div>
<div>space:<input type="text" @keyup.32="keys('keycode 32 space')" /> </div>
<div>space:<input type="text" @keyup.customSpace="keys('keycode 32 space')" /> </div>
</div>
</div>
</template>`, { parseOptions: { html: true } })
复制代码
- 使用find、each、attr API遍历所有的标签以及它的属性,使用keyCodeMap,替换属性名称
//find+each遍历所有的标签项
templateAst.find(['<$_$></$_$>', '<$_$ />']).each((node) => {
//如果节点含有属性,则遍历它的属性
if (Array.isArray(node.attr('content.attributes'))) {
node.attr('content.attributes').forEach((attr) => {
//使用上文构造出来的汇总keyCodeMap,替换匹配到的属性名 如@keyup.32 -> @keyup.space
for (let keyItem in keyCodeMap) {
if (attr.key.content.endsWith(`.${keyItem}`)) {
attr.key.content = attr.key.content.replace(`.${keyItem}`,`.${keyCodeMap[keyItem]}`)
}
}
})
}
})
复制代码
这里用到的node.attr('content.attributes'),就是刚才从astexplorer.net工具查到的
- 最后输出,大功告成,对比一下转换的结果
4 总结
这段代码里的AST相关操作只有10行左右,其余都是核心转换逻辑。GoGoCode可以像使用Jquery操作DOM的一样的体验来操作AST,入门简单使用也方便
starter里还有批量处理文件的demo github.com/thx/gogocod…
Github上提issues也很快就有回应! github.com/thx/gogocod…
实在是代码转换利器
如果文中有任何问题,欢迎您的反馈。
谢谢,祝你有美好的一天!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。