项目整理中,完成后补上Github连接
Typescript正普遍成为前端工程师开发项目的首选,我手头上有一些使用js编写Vue项目,最近准备使用ts重写。项目中单单是页面的数量就超过100个,更不用提组件的数量,若是对这么多Vue文件进行一一重写的话,工程量浩大,而且十分枯燥。其实在此以前也手动转换过几个项目,发现重写过程都是类似的,经过代码是有可能自动地完成重写。固然从js转换到ts下,不可避免地会出现类型问题,如今的自动重写程序只要求完成重复性地工做,当真的须要类型信息时,仍是须要手动处理。javascript
使用ts来编写项目时,可使用两种不一样的代码风格:前端
具体应该选择哪一种方案,见仁见智。我所采用的是方法2。为何选择它,若是使用方法1的话重写起来岂不是很方便?选择方法2是由于在Vue中大量使用this
关键字,使用class形式更加符合直觉——全部的内容都是在class实例上。vue
实现思路就和把大象装进冰箱同样简单:java
关于什么是抽象语法树,能够在网上查找相关资料详细了解(我以为对于抽象语法树有必定的了解是颇有必要的)。简单来讲,js代码能够用一个树形结构表示,这个树形结构就是抽象语法树。例如:git
function foo() { return a + b; }
对应的AST多是下图这样的github
<img src="https://static.playground.forzoom.tech/article/2.png" />web
若是但愿将代码中的b修改成c,那么只须要修改树中的节点就能够,例如这样:typescript
<img src="https://static.playground.forzoom.tech/article/1.png" />npm
以后再生成代码就能够了。api
recast是一个能够方便对代码和AST进行转换的库,能够帮咱们打开冰箱门和关上冰箱门。
这里必须再提到两个概念,分别是estree和ast-types。
estree是将js代码解析成AST的一个社区标准,也就是,最终生成的AST节点中有哪些值,目前基本上都应该参照estree中的说明进行实现。对这个标准有一些的了解,或者说对于编译原理有必定的了解,能够提升以后修改代码的效率。
ast-types是recast中所使用的库,提供了语法树节点定义、遍历等功能。ast-types中所定义的类型兼容estree,但实际使用中,感受有时会有一些缺失,例如在某些状况下,会存在decorators字段不存在的状况,能够经过d.ts文件对ast-types中的类型定义进行扩展。
若是对于编译原理了解的不是那么清楚的话,那么也能够经过recast.parse一些代码,来了解应该如何写,以后依葫芦画瓢编写代码就能够。
在recast.parse解析代码时,会默认使用esprima来进行语法解析,esprima(目前为4.0.1版本)对js新语法已经有了较多的支持,可是对于目前的项目中说,仍是有部分语法没法解析。为了解决这个问题,recast也能够自定义所使用的语法解析器。
我还找到另外两个语法解析库,分别是@typescript-eslint/typescript-estree和@babel/parser,其中@typescript-eslint/typescript-estree对于目前vue-property-decorator中使用的修饰器语法并不支持,因此最终选择@babel/parser。
// 使用自定义的语法解析库 const ast = recast.parse(jsScript, { parser: { parse(source: string, options: any) { return parser.parse(source, Object.assign(options, { plugins: [ 'estree', // 支持estree格式 'decorators-legacy', // 支持修饰器语法 ], tokens: true, // 必要的参数。默认为false,解析结果中缺乏tokens内容,当缺乏tokens时,recast将会从新使用esprima进行解析操做 })) }, }, tabWidth: 4, });
在Node中使用fs来完成对于文件的遍历
import fs from 'fs'; const dir = '/Volumes/Repo2/repo/vue/tourye_web_ts/src'; const dist = '/Volumes/Repo2/repo/vue/tourye_web_ts_ast/src'; const pageDir = dir + '/pages'; const queue = [ pageDir ]; while (queue.length > 0) { const filePath = queue.shift(); if (filePath) { const stats = fs.statSync(filePath); const isDirectory = stats.isDirectory(); if (isDirectory) { // 若是是文件夹,将全部的子路径加入queue const children = fs.readdirSync(filePath); queue.unshift(...children.map(child => filePath + '/' + child)); } else { // 若是是文件,判断是不是.vue文件 if (filePath.indexOf('.vue') >= 0) { const output = dist + filePath.substr(dir.length); fs.mkdirSync(path.dirname(output), { recursive: true, mode: 0o755, }); handleVue(filePath, output); // 对于vue文件进行处理 } } } }
既然能够完成迁移到ts语法的过程,在Vue@3正式发布以后,可能会考虑是否能将旧代码,转换成composition-api的格式。