最近有个想法,以前用Angular2.x 的时候,官方提供了ng-cli 能够一键生成component、service、directive 等代码文件,而且还能够修改对应的routes 配置文件,使得组件自动加入app 的前端路由中(一键生成或修改数个文件)。这使得前端开发效率大为提升,咱们没必要再手动去建立那么多文件夹、文件,而且手动修改route 配置。可是vue-cli 没有提供这种功能,因此咱们想要写个node.js 脚本去作这个工做。除了用正则替换的解决办法,更科学的实际上就须要用到修改代码抽象语法树的方法。html
咱们一般用 babel 去编译ES6/ES7 为ES5,以便于 js 脚本运行在各类浏览器上。这个编译的过程其实是语法转换的过程,好比箭头函数转为函数表达式,this 的显式绑定等等。那么babel 在作这个工做的时候实际上经历了几个步骤,parse => transform (AST) => generate前端
因此要想完成这几个步骤,babel 提供了几个实用工具(Babylon,babel-traverse,babel-generator),咱们的思路就是找到route 配置表中该插入新路由的地方,插入新路由而且保存文件。 babel.transform 核心函数接受源代码字符串和options 做为输入,返回一个Object 包含几个属性:新的代码字符串,sourcemap,ast 语法树对象。vue
babel.transform("code();", options, function(err, result) {
result.code;
result.map;
result.ast;
});
复制代码
如下是等待修改的路由配置文件,node
// './src/router.ts'
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
}
// to be append new route.
]
})
复制代码
如下是经过 babel 修改route 配置文件的过程vue-cli
var fs = require('fs');
let babel = require('babel-core');
let t = require('babel-types');
let template = require('@babel/template');
// 读取须要修改的源代码内容
var content = fs.readFileSync('./src/router.ts').toString();
const newRoute = {
path: '/list',
name: 'list'
// component: ListComponent
};
// 定义一个 babel 插件,拦截并修改 routes 的数组表达式
let visitor = {
ArrayExpression(path) {
const elements = path.node.elements;
console.warn(`routes number: ${elements.length}`);
// 新增一个构建出来的 route 对象
elements.push(t.objectExpression([
t.objectProperty(t.identifier('path'), t.stringLiteral(newRoute.path)),
t.objectProperty(t.identifier('name'), t.stringLiteral(newRoute.name)),
t.objectProperty(t.identifier('component'), t.identifier('ListComponent'))
]));
}
}
// 经过 plugin 转换源代码 parse 出来的AST 抽象语法树,而且返回结果
let result= babel.transform(content, {
plugins: [
{ visitor }
]
});
console.warn(`res: ${result.code}`);
// 把新代码写入新文件.
fs.writeFileSync('newRoute.ts', result.code);
复制代码
比较关键的部分就是在visitor 这个自定义的插件中,拦截ArrayExpression,这是routes: [] 对应的路由数组。而这个数组表达式包含了一个elements 数组,每一个对象在AST 中都是ObjectExpression 类型。不管是数组表达式,仍是对象表达式,都是对应 babel-types 中不一样的节点(node)类型。因此咱们在构建新的 AST 节点时,能够参考AST explorer 中已有的节点类型。数组
例如这里咱们要新增一个route 对象,则是用babel-types 中的 types.objectExpression(objectProperty[]) 生成一个,根据智能提示传入参数,要求是objectProperty 数组,那么咱们又利用 types.objectProperty(key: identifier, value: string) 生成一个。浏览器
t.objectExpression([
// 对象中的第一个属性 path: string;
t.objectProperty(t.identifier('path'), t.stringLiteral(newRoute.path)),
// ...
]);
复制代码
根据官方docs,也能够利用 AST explorer 去寻找对应关系,结合 IDE 的智能提示来构建你所须要的 AST 语法树,就能够自动转换成你想要的代码了。实际上,搞懂了babel ,就能够作出ng-cli generate 这样智能高效的功能了。bash
www.sitepoint.com/understandi… welefen.com/post/unders… babel 官方文档 AST Explorer babel插件入门-AST(抽象语法树)babel