vue语法自动翻译成typescript(免提)
代码重用是非常常见的事情。如果是公共代码的复用,很容易说直接做成内部私有库。如果你想使用它,你可以安装npm包,但是业务代码的重用并不容易做成包。通常是复制粘贴
我平时写代码的时候,如果觉得某个业务代码之前已经被别人写过了,那么考虑到业务优先级,只要别人的代码不太差,我一般会先抄别人的代码,省得自己再写。
然后我遇到了一个问题。目前公司大部分前端项目都是vue。在早期,没有ts这种东西。后来ts逐渐被引入到新项目中,所以vue-ts被用在新项目中,ts没有被引入到我一般想复制的旧代码中。当然两者是兼容的,但是对于有轻微代码洁癖的我来说,我还是不希望看到同一个项目代码夹杂着ts和非ts,所以只要有时间,我会尽量把旧代码手工转换成ts规范。不是太难,但是每一个都要手动翻,我一下子陷入了沉思。我好像在重复我自己,我受不了了,所以我决定写一个工具,自动把vue-js转换成vue-ts
这个工具的代码我已经放在github上了,为了使用方便,我把它做成了一个npm包,有兴趣的可以自己试试
@babel
说到js语法转换,首先想到的是babel。babel长期以来为js语法提供了丰富而完善的解析和反解析工具
@babel/parser
@babel/parser是一个负责解析js语法的工具,可以理解为将js语法转换成ast,方便开发者自定义。插件支持各种js语法,比如es6、es7、ts、flow、jsx甚至一些实验性的语言建议。
例如:
const code=' const a=1 ' const ast=require(' @ babel/parser ')。解析后的ast(代码)是一个对象,数据结构描述了表达式const a=1
通过遍历这个ast,您可以获得当前解析的js语法的所有信息,当然您也可以修改它。
@ babel/发电机
有解析就有反解析。@babel/generator用于将@babel/parser解析的ast转换回字符串形式的js代码
const代码=' const a=1;'const ast=require(' @ babel/parser ')。parse(code)const codestr=require(' @ babel/generator ')。默认(ast)。code code===code str//=true others
一般会一起使用@babel/parser、@babel/generator和@babel/traverse。前两个已经介绍过了。至于@babel/traverse,它的主要功能是遍历@babel/parser生成的ast,提供了一些方法,让开发人员不用自己做各种判断。
但是,我在这里写的程序不需要太详细的分析,所以不使用@babel/traverse。我按照自己的意愿穿越时空
此外,babel还提供了一些其他的工具库和帮助库,一般都不是很有用。如果你想了解更多,你可以自己看文件
本文下面描述的操作基本上是在@babel/parser转换的ast和@babel/generator解析的代码串上执行的
小道具
Vue官网对道具的介绍是在道具
因此,以下书写道具的方式是符合规范的:
导出默认值{ prop :[' size ',' myMessage'],props: { a: Number,b: [Number,String],c: 'defaultValue ',D: {type: [number,string]} e: {type: number,default : 0 0,required: true,validator 3360 function(value){ return value=0 } } }以上到ts的转换对应于以下内容:
导出默认类YourComponent扩展Vue { @ Prop()readonly size : any | undefined @ Prop()readonly my message : any | undefined @ Prop({ type : Number })readonly a : Number | undefined @ Prop([Number,String])readonly b : Number | String | undefined @ Prop()readonly c! any @ Prop({ type :[Number,String]})readonly d : Number | String | undefined @ Prop({ type : Number,default : 0 0,required: true,validator: function (value) {返回值=0 } }) readonly e!号}好的,这很容易做到。首先,道具值的类型只有Arraystring和object
数组类型
Arraystring类型很容易处理,只是一个转换模板:
@ prop () readonly propsName :任何|未定义的只是遍历Arraystring类型的道具,然后用真实值替换道具名称
对象类型
在数组类型的模板上,对象类型的转换模板增加了一些字符串,主要是@Prop的参数:
@ prop ({type:typev,default:defaultv,required d : required dv,validator : validator v })readonly prosname 3360 typevprops这个大对象的每个属性,它们都是一个prosname,这是一定的,然后prosname的对应值可能是type,它分为单一类型(比如Number)和类型数组(比如[number,string]);它可能是一个至少有0个属性且最多有4个属性的对象。如果这个对象中有一个名为type的属性,这个属性的值也需要确定单一类型和类型数组,其他属性可以直接取原始值。
无论props对象的属性值是对象还是类型,都需要处理类型,所以一个专门处理类型的方法handlerType
这样,如果是type,直接处理handlerType如果它是一个对象,遍历该对象的属性,如果发现该属性是类型,则调用
处理HandlerType,否则可以直接作为@Prop的参数
数据
Vue官网对数据的介绍是在数据
数据的类型可以是对象,也可以是函数,也就是说,下面的编写方法是合法的:
导出默认值{data: {a: 1},data () {return {a: 1}},data : function(){ return { a : 1 } } }以上到ts的转换对应于以下内容:
导出默认类你的组件扩展了vue {A: number=1},所以这里很明确,就是把数据返回值对象的每个属性都作为类的属性,好像要转换一样。
然而,数据实际上可以这样写:
export default { data(){ const origin=1 return { a : origin } } }当数据为Function类型时,可以在返回之前运行一段代码,这段代码的运行结果可能会影响数据的值
这种写法并不少见,所以不能忽视,但是返回之前的代码怎么处理呢?
我的方法是将返回前的代码放入已创建的生命周期函数中,并在创建这些代码后,再次为每个数据赋值。例如,对于上面的代码,要转换为ts,您可以这样做:
导出您的组件扩展的默认类vue { a : any=null created(){ const origin=1 this。a=origina } }因此,这涉及到数据修改所创建的数据。在这里,我们可以考虑先强制处理数据,但我已经看过了。其实这里写两段逻辑并不复杂,所以我就不严格规定处理的顺序了
模型
Vue官网对模特的介绍是在模特
道具中的价值在模型中被引用,所以模型的使用实际上需要道具配合
导出默认{模型: {道具: '选中',事件: '更改' },道具: {选中3360 {类型:布尔}}上述到ts的转换对应于以下内容:
导出默认类YourComponent扩展Vue { @Model('change ',{ type: Boolean })只读选中!从: boolean}可以看出@Model具有声明道具的功能,无需再次声明@Prop中声明的道具。所以我在这里安排了一个处理顺序,先处理模型,再处理道具,在处理道具的时候,过滤掉已经在模型中声明的道具。
当然,你不必先和模特打交道,再和道具打交道。处理模型时只需判断道具之前是否处理过,根据结果做相应的处理流程。不过这个有点麻烦,需要根据道具是否处理写两段逻辑。这两条逻辑比上面创建的数据影响更复杂,为了省事,我就直接处理了。
计算
Vue官网对模型的介绍在计算中
以下计算书写方式是正确的
导出默认值{ computed: { a () { return true }、b: function () { return true }、d: { get () { return true }、set : function(v){ console . log(v)} } vue-property-decorator不为computed提供特殊修饰符,因为ES6本身的get/set语法可以替换computed。以上到ts的转换对应于以下内容:
导出默认类您的组件扩展vue { get a(){ return true } get b(){ return true },get d () {return true},set d (v) {console.log (v)}}另外,computed实际上支持写箭头函数:
export default { computed : { e :()={ return true } } }但是get/set类语法不支持arrow函数,因此很难转换。另外,因为箭头函数会改变这个方向,而且computed会计算当前vue实例上的属性,所以,一般不建议在computed中使用箭头函数。虽然您可以在arrow函数的第一个参数上获得当前vue实例,但这有点多余。因此,我将在这里跳过箭头函数的处理,只在计算时遇到箭头函数时给你提示。
看
Vue官网对手表的介绍是在看
以下是法律观察写作:
export default { watch : { a : function(val,oldval) {console.log ('new3360% s,old:% s ',val,oldval)},//方法名b: 'someMethod ',//当任何被拦截对象的属性发生变化时都会调用此回调,无论嵌套的深度有多深,c : { handler : function(val,old){/*.*/},deep: true},//回调将被调用d: {handler:' somemethod ',immediate: true},e :[' handler 1 ',function handler 2(val,oldval) {/*.*/}听完后立即。{handler:函数handle3 (val,oldval) {/*.*/},immediate: true}],//watchvm.e.f的值: {g: 5}' e.f' :函数(val,oldval)
导出默认类YourComponent扩展Vue { @ Watch(' a ')on changed(val : any,oldVal : any){ } @ Watch(' b ')on changed(val : any,oldVal : any){ this . some method(val,oldVal) } @Watch('c ',{ deep 3: true })on changed(val 3: any,oldVal: any) {} @Watch('d ',{ deep 3: true })
被监视的每个属性都是一个需要被监视的vue响应值。这些属性值可以是字符串、函数、对象和数组。有四种类型。字符串类型相当于调用当前vue实例中的方法,函数类型是调用这个函数,比较简单;
至于对象类型,它有三个属性:handler、deep和immediate,都是可选的,其中handler的值是函数或字符串,另外两个属性的值是布尔值;
对于数组类型,每个数组项实际上相当于字符串类型、函数类型和对象类型的聚合,所以实际上只要处理这三种类型,数组类型就直接遍历数组项,每个数组项的类型都必须在这三种类型之内,所以可以根据类型调用相应的处理方法。
这是主要部分。此外,还需要考虑处理函数的形式。以下功能是合法编写的:
导出默认值{ watch: { a:function {、b () {}、c: ()={}、d:aasync function {}、e:aasync ()={}}不仅存在于watch中,还存在于其他vue实例属性中。例如,创建、计算等。只要功能可能出现,这些编写方法都需要考虑。当然,除此之外,还有Generator函数,不过我这里不考虑。有更好的异步/等待可用。为什么一定要用发电机
方法
vue实例的方法都是作为对象方法的属性存在的,每个方法都是一个函数,只需要把原来方法下的方法都拿出来转换成class的方法,没有工作量
但是,应该注意的是,有很多方法可以编写函数,您也可以支持异步/等待,这需要考虑
生命周期
vue的生命周期中有很多钩子函数,也有一些第三方的钩子函数,比如vue-router:
const vuelifeccycle=[' before create ',' created ',' beforeMount ',' mounted ',' beforeUpdate ',' updated ',' activated ',' deactivated ',' beforeDestroy ',' Destroyed ',' errorcaptured ',' beforerouteenter ',' before routeupdate ']这些钩子函数实际上是函数
成分
这相对简单,转换它,然后拼接它
导出默认值{components: {a:a,b},}上述到ts的转换对应于以下内容:
@ component({ components 3360 { a : a,b}})导出默认类transvue2ts扩展了vue {},因此原始组件的所有属性都可以映射一次。
混入类
Vue官网对mixin的介绍是在mixin
它的值类型是ArrayObject
导出默认值{ mixin :[a,b]}上述到ts的转换对应于以下内容:
导出默认类transvue2ts扩展mixin(a,b) {}原来的extends Vue改为extends mixin,mixin的参数都是原来mixin的数组项
提供注射
当我想到如何处理这两个的时候,我看了看vue官网,官网对这两个说了这样的话:
提供和注入主要提供高级插件/组件库的用例。不建议在应用程序代码中直接使用。
而在这段话中,也是用红色的感叹号标注的。说白了,不建议你在业务代码里面,因为不利于数据跟踪,完全可以用成熟的vueBus或者vuex来代替,一般你都不会用这个东西。我编写这个转换程序是为了转换业务代码,所以没有处理这两个属性。如果您在代码中找到这两个属性,系统会提示您手动处理它们。
发出参考
两者都是类似语法糖的东西,可以不加处理
文件处理
以上是详细处理a. vue文件的逻辑。如果要访问实际的文件甚至文件夹处理,就必须读取并更新文件,这涉及到节点的文件处理内容,但并不复杂,我就不多说了。
Npm包
代码写好后,为了简化使用过程,我把它打包成一个npm包,上传到npm。如果我想使用它,我只需要下载包并在命令行中输入说明
NPM I trans ue2ts-g安装后,默认与vue-cli相同,这个库的路径会写入系统的路径中,直接打开命令行工具即可使用。同时,transvue2ts是库的指令,第二个参数是待处理文件(文件夹)的完整完整路径,如处理E3360 \ project \ testa \ src \
trans ue2ts e : \ project \ TeSta \ src \ test . vue=输出路径:e : \ project \ TeSta \ src \ test ts . vue处理所有。e: \ project \ testa \ src文件夹下的vue文件:
trans user 2ts e : \ project \ testA \ src=输出路径:E:\project\testA\srcTs对于单个文件,它必须是的结尾。vue,转换后的文件将输出到与原文件名ts相同级别的目录,例如index . vue=indexts . vue;至于文件目录,程序会递归遍历这个文件目录,找出所有的。vue文件在此文件夹中进行转换,所有转换后的文件将根据原始目录结构转换到同一级别目录下的新文件夹中,例如/src=/srcTs
摘要
这个改造方案看起来很麻烦。总结起来,其实就是三个步骤:
列举所有需要转换的vue-js语法及其可变的编写方法列举js-ts语法之间的转换映射关系。编写语法转换代码本质上是一个翻译器,把vue-js语法翻译成vue-ts语法。难点在于你要找到它们之间所有的映射关系,知道如何处理,所以其实大部分都是手工的。
只要了解了套路,其实把vue改成wepy,或者反应到微信小程序,其实都是一样的,所有的翻译员,所有的体力活动,但有些都很轻松,那就是搬几块砖。事情,还有一些体力活动都比较辛苦,需要动动脑筋
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:vue语法自动翻译成typescript(免提)是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。