最近有個想法,以前用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