Vue从js迁移到ts

项目整理中,完成后补上Github连接

Typescript正普遍成为前端工程师开发项目的首选,我手头上有一些使用js编写Vue项目,最近准备使用ts重写。项目中单单是页面的数量就超过100个,更不用提组件的数量,若是对这么多Vue文件进行一一重写的话,工程量浩大,而且十分枯燥。其实在此以前也手动转换过几个项目,发现重写过程都是类似的,经过代码是有可能自动地完成重写。固然从js转换到ts下,不可避免地会出现类型问题,如今的自动重写程序只要求完成重复性地工做,当真的须要类型信息时,仍是须要手动处理。javascript

使用ts来编写项目时,可使用两种不一样的代码风格:前端

  1. 使用Vue.extend方法实现。
  2. 使用class语法配合vue-property-decorator实现。

具体应该选择哪一种方案,见仁见智。我所采用的是方法2。为何选择它,若是使用方法1的话重写起来岂不是很方便?选择方法2是由于在Vue中大量使用this关键字,使用class形式更加符合直觉——全部的内容都是在class实例上。vue

实现思路

实现思路就和把大象装进冰箱同样简单:java

  1. 将旧代码转换成AST(Abstract Syntax Tree, 抽象语法树)。
  2. 将AST修改为class形式。(类型信息天然无法所有填上,能够先用any或者选择不填写)
  3. 将AST转换成新代码。

关于什么是抽象语法树,能够在网上查找相关资料详细了解(我以为对于抽象语法树有必定的了解是颇有必要的)。简单来讲,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

代码和AST的转换

recast是一个能够方便对代码和AST进行转换的库,能够帮咱们打开冰箱门和关上冰箱门。

这里必须再提到两个概念,分别是estreeast-types

estree是将js代码解析成AST的一个社区标准,也就是,最终生成的AST节点中有哪些值,目前基本上都应该参照estree中的说明进行实现。对这个标准有一些的了解,或者说对于编译原理有必定的了解,能够提升以后修改代码的效率。

ast-types是recast中所使用的库,提供了语法树节点定义、遍历等功能。ast-types中所定义的类型兼容estree,但实际使用中,感受有时会有一些缺失,例如在某些状况下,会存在decorators字段不存在的状况,能够经过d.ts文件对ast-types中的类型定义进行扩展。

若是对于编译原理了解的不是那么清楚的话,那么也能够经过recast.parse一些代码,来了解应该如何写,以后依葫芦画瓢编写代码就能够。

选择parser

在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的格式。

相关文章
相关标签/搜索