如何使用 GoGoCode 一键 Vue2 转换 Vue3
前言
从今年年初开始,项目开始升级优化,将之前的 Vue2 旧版本整体升级到 Vue3 版本。在重写了几个 Vue 文件后,我发现做的都是一些机械性的工作,效率低且重复性大。于是就试着搜索了一下有没有什么能够批量转换代码格式的工具,发现了阿里的这个基于 AST 的 JavaScript/Typescript/HTML 代码转换工具——GoGoCode。
时间已经过去大半年了,写篇博客记录一下,希望能帮到有需要的朋友。
官网上关于 Vue2 升级 Vue3 大抵有两种方案。第一种介绍了一套 Vue2 升级工具,利用这套工具能够快速地把 Vue2 代码升级到 Vue3。但是过程较为繁琐,出现错误的可能性较大,网上也没有什么详细介绍和成功案例,最主要是我没有成功的运行。第二种则就是普通的转换,利用自己编写的转换规则按部就班转换。所以我选择了第二种,灵活性较大,可以根据各个项目的特点按需要编写转换规则,然后规规矩矩逐个文件代码转换,转换完成之后对比有无错误语法,再运行测试是否有缺陷,比较直观。
安装插件
在 VSCode 拓展中搜索 GoGoCode 并安装即可。
在安装完成之后,就可以在需要转换的 Vue2 文件上点击鼠标右键,选择“用 GoGoCode 转换”,即可打开“GoGoCode PlayGround”。
可以看到四块内容,左上方显示的是源码,右上方为可以编辑的转换规则,下方的两块内容则是转换前后代码的对比。
操作过程
构造实例
调用 $(code, options) 即可将一段代码或一个 ast 节点构造为 GoGoCode 的核心 AST 实例。
其中 code 为需要被实例化的代码或 AST 节点,options 为一个对象,包括 parseOptions、astFragment、isProgram。
入参 | 说明 | 类型 | 举例 | |
---|---|---|---|---|
code | 需要被实例化的代码或 AST 节点 | string NodePath Node | 'var a = 1' | |
options | parseOptions | 解析 js 时,它与 babel/parse 的 options 完全一致 解析 html、vue 时需要传入 language |
object | { plugins: ['jsx'] } { language: 'html' } |
astFragment | 需要插入到代码中的 ast 节点 | Node | ||
isProgram | 是否需要返回完整 ast js 的完整 ast 最外层节点是 File 类型 html 完整 ast 是 document 类型 |
Boolean | 默认为 true |
因此,我们可以这样写将 vue2 文件中的代码构造为 AST 实例。
const ast = $(source, { parseOptions: { language: "vue" } });
获取节点
所有的节点获取操作都会返回一个新的 AST 实例。
ast.find
在把代码从字符串解析成 AST 后,我们进入第二步,从一整段代码中精确查找到我们要修改的 AST 节点。
GoGoCode 提供了直观的「用代码找代码」的方式,和 jQuery 查找 DOM 一样,你只需要编写一段代码片段作为「代码选择器」,GoGoCode 就能智能地帮你匹配到源码中和它吻合的片段。
例如我们使用.find()方法,分别查找 template 和 script 各作为一个整体 ,方便后续操作。
最后用 generate 把节点输出成代码字符串。
function transform(fileInfo, api, options) {
const $ = api.gogocode;
const source = fileInfo.source;
const ast = $(source, { parseOptions: { language: "vue" } });
const template = ast.find("<template></template>");
const script = ast.find("<script></script>");
return ast.generate();
}
操作节点
将获取到的节点通过编写的规则转换,将是我们的第三步。不过在此之前,我们需要了解一下通配符和.replace()方法。
$_$ 通配符
假设你想在下面代码中挑选出对于变量 a 的声明和初始化语句:
const a = 123;
按照之前介绍的,我们只要像下面这么写就可以了:
const aDef = ast.find("const a = 123");
但这只能匹配到 const a = 123,对于 const a = 456 就无能为力了,在实际的代码匹配中,我们往往不确定代码的全貌,这时候 GoGoCode 支持使用通配符来做模糊匹配:
const aDef = ast.find("const a = $_$0");
有时我们不止需要一个通配符,你可以在代码选择器中书写 $_$0、$_$1、$_$2、$_$3……达到你的目的。
$$$ 通配符
假设有下面的代码:
console.log(a);
console.log(a, b);
console.log(a, b, c);
按照之前的写法,我们可以用以下几种选择选择器进行查找。
ast.find(`console.log()`);
ast.find(`console.log($_$0)`);
// 上面两条语句会找到全部三行代码
ast.find(`console.log($_$0, $_$1)`);
// 这条语句会找到前两行代码
ast.find(`console.log($_$0, $_$1, $_$2)`);
// 这条语句只会找到第三行代码
可以看出 GoGoCode 的通配符匹配的原则:写得越多,查询限制越大。
如果你想匹配任意数量的同类型节点,GoGoCode 提供了 $$$ 形式的通配符,对于上面不定参数的语句,你可以统一使用 ast.find('console.log($$$0)') 来匹配。
ast.replace
日常我们在编辑器中批量修改代码的时候也会经常使用到「查找\替换」的功能去做一些基本操作,但它们都基于字符串或正则表达式,对于不同的缩进、换行乃至加不加分号都无法兼容,而利用 GoGoCode 的代码选择器特性配合 replace 方法,可以让你以接近字符串替换的形式完成 AST 级别的代码替换操作。
function log(a) {
console.log(a);
}
function alert(a) {
alert(a);
}
如果我们想给 log 函数改名成 record,用 replace 做非常简单:
ast.replace("function log($$$0) { $$$1 }", "function record($$$0) { $$$1 }");
现在,我们就可以开始编写规则了。目的是将 vue2 中的语法规则升级为 Vue3 的语法规则,同时将 ui 组件库 element-ui 升级为 element-plus,主要分为以下几部分。
1.插槽。Vue3 中需要在外面包裹上一层 tempplate,写法也由内层的 slot="xxx" 变为外层的 #xxx。
template.replace(
'<$_$ slot="filter" $$$0>$$$1</$_$>',
"<template #filter><$_$ $$$0>$$$1</$_$></template>"
);
template.replace(
'<el-dropdown-menu slot="dropdown" $$$0>$$$1</el-dropdown-menu>',
"<template #dropdown><el-dropdown-menu $$$0>$$$1</el-dropdown-menu></template>"
);
- el-button type="text" 需要转换为 el-button type="primary" link。
template.replace(
'<el-button type="text" $$$0>$$$1</el-button>',
'<el-button type="primary" link $$$0>$$$1</el-button>'
);
3.去掉 export default{}。
script.replace("export default {$$$}", "$$$");
4.去掉 components、mixins。
script.replace("components:{}", "").replace("mixins:[]", "");
5.去掉 props,并将里面的'props:{xxx}'的形式转换为'const props=defineProps({xxx})'的形式。
script.replace("props:{$$$}", "const props=defineProps({$$$})");
6.去掉 data、return,并将里面'xxx:yyy'的形式转换为'let xxx=$ref(yyy)'的形式。
script.find("return {}").replace("$_$:$_$", "let $_$=$ref($_$)");
7.去掉 conputed,并将里面'xxx(){}'的形式转换为'const xxx=computed(()=>{})'的形式。
script
.find("computed:{}")
.replace("$_$(){$$$}", "const $_$=computed(()=>{$$$})");
8.去掉 watch,由于 watch 有多种写法:有无 deep、有无 handler,所以这里按顺序都走了一遍。
script
.find("watch:{}")
.replace("$_$:{handler($_$){$$$}}", "watch(()=>$_$,($_$)=>{$$$})")
.replace("$_$:{handler(){$$$}}", "watch(()=>$_$,()=>{$$$})")
.replace(
"'$_$':{handler($_$){$$$},deep:true}",
"watch(()=>$_$,($_$)=>{$$$},{deep:true})"
)
.replace("'$_$':{handler($_$){$$$}}", "watch(()=>$_$,($_$)=>{$$$})")
.replace("$_$($_$){$$$}", "watch(()=>$_$,($_$)=>{$$$})")
.replace("$_$(){$$$}", "watch(()=>$_$,()=>{$$$})")
.replace("watch:{$$$}", "$$$");
9.去掉 filters,两种写法都走一遍。
script
.find("filters:{}")
.replace("$_$:function($_$){$$$}", "const $_$=computed(($_$)=>{$$$})")
.replace("$_$($_$){$$$}", "const $_$=computed(($_$)=>{$$$})")
.replace("filters:{$$$}", "$$$");
10.去掉 methods,同样有多种可能存在的形式:有无 async、有无参数,按顺序都走一遍。
script
.find("methods:{}")
.replace("async $_$($$$0){$$$1}", "const $_$=async($$$0)=>{$$$1}")
.replace("$_$($$$0){$$$1}", "const $_$=($$$0)=>{$$$1}")
.replace("async $_$(){$$$}", "const $_$=async()=>{$$$}")
.replace("$_$(){$$$}", "const $_$=()=>{$$$}")
.replace("methods:{$$$}", "$$$");
10.生命周期的改变。
script
.replace("created(){$$$}", "onBeforeMount(()=>{$$$})")
.replace("mounted(){$$$}", "onMounted(()=>{$$$})")
.replace("beforeUnmount(){$$$}", "onBeforeUnmount(()=>{$$$})")
.replace("unmounted(){$$$}", "onUnmounted(()=>{$$$})")
.replace("beforeDestroy(){$$$}", "onBeforeUnmount(()=>{$$$})")
.replace("destoryed(){$$$}", "onUnmounted(()=>{$$$})");
成果展示
如何使用 GoGoCode 一键 Vue2 转换 Vue3的更多相关文章
- Vue2和Vue3技术整理1 - 入门篇 - 更新完毕
Vue2 0.前言 首先说明:要直接上手简单得很,看官网熟悉大概有哪些东西.怎么用的,然后简单练一下就可以做出程序来了,最多两天,无论Vue2还是Vue3,就都完全可以了,Vue3就是比Vue2多了一 ...
- vue2升级vue3:vue2 vue-i18n 升级到vue3搭配VueI18n v9
项目从vue2 升级vue3,VueI18n需要做适当的调整.主要是Vue I18n v8.x 到Vue I18n v9 or later 的变化,其中初始化: 具体可以参看:https://vue- ...
- vue2和vue3生命周期的区别
概念 首先,我们了解一下"生命周期"这个词.通俗的来说,生命周期就是一个事务从出生到消失的过程.例如,一个人从出生到去世.在vue中,vue的生命周期是指,从创建vue对象到销毁v ...
- vue2升级vue3指南(二)—— 语法warning&error篇
本文总结了vue2升级vue3可能会遇到的语法警告和错误,如果想知道怎样升级,可以查看我的上一篇文章:vue2升级vue3指南(一)-- 环境准备和构建篇 Warning 1.deep /deep/和 ...
- vue2升级vue3:Vue Demij打通vue2与vue3壁垒,构建通用组件
如果你的vue2代码之前是使用vue-class-component 类组件模式写的.选择可以使用 https://github.com/facing-dev/vue-facing-decorator ...
- 盘点Vue2和Vue3的10种组件通信方式(值得收藏)
Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异:本文将通过选项式API 组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式.其中将要实现的通信 ...
- vue2和vue3的区别?
vue2和vue3的主要区别在于以下几点: 1.生命周期函数钩子不同 2.数据双向绑定原理不同 3.定义变量和方法不同 4.指令和插槽的使用不同 5.API类型不同 6.是否支持碎片 7.父子组件之间 ...
- Vue2 到 Vue3,重温这 5 个常用的 API
距离Vue3发布已经过去一年多时间了,从Vue2到Vue3是一个不小的升级,包括周边生态等.虽然目前大多数开发者们在使用的仍旧以Vue2为准,但Vue3显然是Vue开发者们未来必须面对的,而且前不久V ...
- vue2和vue3区别
1. vue2和vue3双向数据绑定原理发生了改变 vue2的双向数据绑定是利用了es5 的一个API Object.definepropert() 对数据进行劫持 结合发布订阅模式来实现的.vue3 ...
- vue2升级vue3:vue-i18n国际化异步按需加载
vue2异步加载之前说过,vue3还是之前的方法,只是把 i18n.setLocaleMessage改为i18n.global.setLocaleMessage 但是本文还是详细说一遍: 为什么需要异 ...
随机推荐
- Linux系统内核的作用
Linux系统内核在操作系统中扮演着至关重要的角色,其作用主要体现在以下几个方面: 进程管理:内核负责创建和销毁进程,这是操作系统对计算机上正在运行的程序进行管理的核心部分.内核通过调度器对进程进 ...
- Sy.ExpressionBuilder 动态查询新体验
省流模式,看下对比 //常规查询 var query = users .WhereIf(m => m.UserName.Contains(input.UserName), !string.IsN ...
- ARM架构及ARM指令集、Thumb指令集你了解多少?
https://www.sohu.com/a/339622340_100281310 1991 年ARM 公司成立于英国剑桥,在成立后的那几年,ARM业绩平平,工程师们也人心惶惶,害怕随时都会失业.在 ...
- 如何让你的C语言程序打印的log多一点色彩?(超级实用)
接着上一篇文章<由字节对齐引发的一场"血案" > 在平常的调试中,printf字体格式与颜色都是默认一致的. 如果可以根据log信息的重要程度,配以不同的颜色与格式,可 ...
- 【CMake系列】03-cmake 注释、常用指令 message、set、file、for_each、流程控制if
本文给出了 cmake 中的 一些常用的 指令,可以快速了解,为后面的内容深入 打点基础. 本专栏的详细实践代码全部放在 github 上,欢迎 star !!! 如有问题,欢迎留言.或加群[3927 ...
- 9k star 监控系统,100% 国产,推荐了解
前言 监控系统的重要性不言而喻,国内用的最多的应该是 Zabbix 和 Prometheus,其优缺点: Zabbix 是资产管理式,监控数据存在数据库中,擅长设备监控,不擅长微服务和云原生环境的监控 ...
- containerd:配置https私有镜像仓库的最新方法
随着containerd应用越来越广泛,我们必须紧跟官网的节奏. 之前配置https私有镜像仓库的方法比较繁琐,并且不易梳理,下边介绍一下目前最新的配置方法. 配置https私有镜像仓库 我假设你现在 ...
- mvn install 设定下载的依赖保存的地址
mvn clean install -P jdk-1.8 -Dmaven.repo.local=/xxxx/repository
- CMake构建学习笔记14-依赖库管理工具
如果说做C/C++开发最大的痛点是什么,那么一定是缺少一个官方的统一的包管理器.认真的说,如果你要用C/C++干点什么,至少需要(Windows系统下): C/C++语言本身.标准库.以及操作系统AP ...
- bacnet mstp设备数据 转IEC61850项目案例
目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 使用YABE软件读取BACNET MSTP设备信息 2 4 配置网关采集BACNET MSTP数据 4 5 用IEC61850协议转发数据 ...