0成本上手AST,应用GoGoCode解决Vue2迁移Vue3难题

1 为什么要迁移 Vue3.x

说点什么呢?
总之。。。 这不是我的错!
首先先要写个案例,找到一个可执行方案。


image.png

2 方案选择

参考Vue2转3官方文档
https://v3.cn.vuejs.org/guide/migration/introduction.html

面对自己项目百八十个文件,人工爆肝肯定是不可能的,想都不能想!
唯一的方案是基于AST(抽象语法树)解构代码,根据Vue官网给出升级文档的修改建议,批量修改输出文件的方案。
只是。。。AST操作有点复杂。

image.png

调研了几个工具,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 的示例:
(基于篇幅省略了部分代码,有兴趣的同学请访问https://github.com/mattphillips/babel-plugin-console

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+行代码
        */        
      },
    },
  };
}

基于本人实力,这两个方案实在是劝退案例。


image.png

正在我为这个项目重构发愁,发量迅速减少,混迹于社区寻找解救方案的时候,忽然发现了GoGoCode这个工具。

贴一段官方介绍:

GoGoCode是一个操作AST的工具,可以降低使用AST的门槛,帮助开发者从繁琐的AST操作中解放出来,更专注于代码分析转换逻辑的开发。简单的替换甚至不用学习AST,而初步学习了AST节点结构(可参考AST查看器)后就可以完成更复杂的分析转换。

GoGoCode的官方文档 https://gogocode.io/zh/docs/specification/getting-started

这不正是我想要的么,遇到你就是古话说的,瞌睡来了有人递枕头!
而GoGoCode操作 AST 去掉 代码中console.log,仅仅只需要一行代码!!

$(`要转换的代码段`).find(`console.log($_$)`).remove()

熟悉的 $符号,熟悉的find、remove等API,扣一下题,说零成本操作AST不算标题党吧
爆赞!!!方案选定!!!

image.png

3 开工

举个栗子:按键修饰符的迁移方案

Vue2转3官方文档 - 按键修饰符的迁移
https://v3.cn.vuejs.org/guide/migration/keycode-modifiers.html#%E6%A6%82%E8%A7%88

以下是变更的简要总结:

  • 非兼容:不再支持使用数字 (即键码) 作为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>

任务确定

根据https://astexplorer.net分析下要转换的内容
安利一下astexplorer.net这个工具,我们可以利用它很方便的查看某段代码的AST语法树结构

image.png

这里要做的有三件事:
1)提取代码里自定义的keyCodes(上图蓝框Vue.config.keyCodes的内容部分),与系统keyCodes合并为一个map,后续要在template替换时使用;
2)移除Vue.config.keyCodes 代码(Vue3不再支持);
3)遍历所有标签以及它的属性,使用合并后的keyCodes替换标签(上图红框 xx.32与xx.customSpace部分)。

转换逻辑编写

1.项目里运行安装GoGoCode

npm install gogocode
  1. 初始化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);
    },
  },
};`)

3.我们要寻找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工具中验证一下

image.png

image.png

控制台打出customKeyCodeList,是一个Node数组


image.png

4.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构造结果


image.png

5.Vue3要求: Vue.config.keyCodes不再支持,需要移除。find到这个节点,使用remove API移除

scriptAst.find(`Vue.config.keyCodes = $_$`).remove()

6.初始化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 } })

7.使用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工具查到的

image.png

8.最后输出,大功告成,对比一下转换的结果


image.png
image.png

4 总结

这段代码里的AST相关操作只有10行左右,其余都是核心转换逻辑。GoGoCode可以像使用Jquery操作DOM的一样的体验来操作AST,入门简单使用也方便

starter里还有批量处理文件的demo
https://github.com/thx/gogocode/tree/main/packages/gogocode-starter
Github上提issues也很快就有回应!
https://github.com/thx/gogocode/issues?q=is%3Aissue+is%3Aclosed

实在是代码转换利器

如果文中有任何问题,欢迎您的反馈。谢谢,祝你有美好的一天!

GoGoCode 相关链接

GoGoCode的Github仓库(新项目求star _github.com/thx/gogocod…

GoGoCode的官网 gogocode.io/

可以来 playground 快速体验一下 play.gogocode.io/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容