前言我们前几天发布了《阿里妈妈又做了新工具,帮你把 Vue2 代码改成 Vue3 的》。本文分享其中一种转换规则:eventHub(或eventBus)转换的思路。Vue官方的迁移方案:https://v3.cn.vuejs.org/guide/migration/events-api.html1。首先介绍一下eventHub1.1Vue2的eventHubeeventHub是一个组件间共享的事件中心,在Vue中用作组件通信的桥梁,它向eventHub发送消息,其他模块通过订阅这个eventHub获取相应的数据。官网介绍:Vue实例可用于触发事件触发API强制添加的处理程序($on、$off和$once)。事件中心,用于创建整个应用程序可用的全局事件监听器的实现://eventHub.jsconsteventHub=newVue()exportdefaulteventHub//ChildComponent.vueimporteventHubfrom'./eventHub'exportdefault{mounted(){//添加eventHub监听器eventHub.$on('custom-event',()=>{console.log('Customeventtriggered!')})},beforeDestroy(){//移除eventHub监听器eventHub.$off('custom-event')}}1.2Vue3中的eventHub然而在Vue3中,$on,$off和$once方法被正式移除我们完全移除了$来自实例on、$off和$once方法。$emit仍然包含在现有的API中,因为它用于触发由父组件以声明方式添加的事件处理程序。在Vue3中,不再可能使用这些API从组件内部监听组件本身发出的事件,并且没有针对此用例的迁移方法。2.迁移方案2.1eventHub第三方库的选择Vue官方提供了替代方案:使用第三方库,》eventHub方式可以换成外部实现了事件触发接口的库,比如mitt或者tiny-emitter”。研究了连指手套和微型发射器。mitt虽然star数较高,但不支持$once方式,需要结合其他方式实现。为了降低迁移成本,我选择使用tiny-emitter。一个微型(不到1k)的事件发射器库。2.2迁移工具的选择面对N个项目,一百八十个文件,各种eventHub的实现方式,手动替换绝对不是一个可行的方案。更好的解决方案是基于AST(抽象语法树)对代码进行解构,批量修改,然后按照既定规则输出文件。这里推荐GoGoCode,一个强大易用的处理AST的工具。GoGoCode提供了一个类似JQuery的API来操作AST对象。降低AST的使用门槛,帮助开发者从繁琐的AST操作中解放出来,是代码迁移最有力的助手。另外,安利一个离不开AST分析的工具:https://astexplorer.net,使用它,我们可以很方便的查看某段代码的AST语法树结构3.代码迁移3.1写一个Demo待转换A:{{num}}
3.2Thingstodo找到script标签中的$on$off$once$emit方法,提取他们的对象命名对象名,找到声明它的代码段,用tiny_emitter对象覆盖几个eventHub在添加tiny_emitter之前提供的废弃方法引用文件输出转换效果如下:3.3开始安装GoGoCodenpminstallgogocode初始化Vue文件的AST对象//读取文件内容对于ast对象letast=$(`Content待转换的vue文件的`,{parseOptions:{language:'vue'}})//定位ast对象的script节点letscriptAST=ast.find('')转换逻辑:实现“3.2需要做什么”//1.遍历脚本内容$on,$off,$onceand$emitscriptAST.find([`$_$1.$on($_$2)`,`$_$1.$off($_$2)`,`$_$1.$once($_$2)`,`$_$1.$emit($_$2)`].each(hubAST=>{//1.1使用hubAST.attr('callee.object.name')API提取ehb.$xxx(...)的对象名if(hubAST.attr('callee.object.name')){//2.查找eventHub声明位置letdefinitions=scriptAST.find(`import${hubAST.attr('callee.object.name')}from'$_$'`)consteventHub=`Object.assign(${hubAST.attr('callee.object.name')},{$on:(...args)=>tiny_emitter.on(...args),$once:(...args)=>tiny_emitter.once(...args),$off:(...args)=>tiny_emitter.off(...args),$emit:(...args)=>tiny_emitter.emit(...args),});`//3.遍历声明位置,使用afterAPI覆盖之前的eventHub对象定义.each(def=>{if(!scriptAST.has(eventHub)){def.after(eventHub)}})}//4.添加tiny_emitter引用if(!scriptAST.has(`从'tiny-emitter/instance'导入tiny_emitter`)){scriptAST.prepend(`从'tiny-emitter/instance';\n`)}})最后将AST对象输出为字符串:returnast.generate()具体实现代码:playground4.最后总结一下GoGoCode对这些代码实现的AST操作:$.findAPI用于定位$on$off$once$emit并输出AST对象xxx.attr用于提取AST对象的属性,对于例如,在这段代码中获取ehb.$on(......)的对象名称ehb使用hasAPI来判断节点xxx.after是否已经存在于AST中。在将一个AST对象xxx.prepend添加到相应的AST之后。在对应的AST之前添加一个AST对象是一个非常熟悉的操作!下面是一些eventHub转换实现的思路和GoGoCode的使用。希望得到大家的意见和建议。如果您发现任何代码问题,请向我们发送问题https://github.com/thx/gogocode/issues谢谢您继续阅读,祝您有美好的一天!