parse->AST->transform->gengeratejavascript
如何編譯js->ASThtml
語法糖的polyfilljava
代碼統一hacknode
依賴core-js,提供es*->es3的方法,只轉化語法,不轉換API(類Promise,WeakMap)git
babel的基礎配置initgithub
利用pirate對require進行劫持,在hook中進行babel 原理見express
同時對babel後的code進行緩存,提升下次babel效率npm
function compile(code, filename) { ... let cacheKey = `${JSON.stringify(opts)}:${babel.version}`; const env = babel.getEnv(false); if (env) cacheKey += `:${env}`; //讀取緩存 根據mtime判斷是否須要從新babel if (cache) { const cached = cache[cacheKey]; if (cached && cached.mtime === mtime(filename)) { return cached.code; } } const result = babel.transform(code, { ...opts, sourceMaps: opts.sourceMaps === undefined ? "both" : opts.sourceMaps, ast: false, }); if (cache) { cache[cacheKey] = result; result.mtime = mtime(filename); } if (result.map) { if (Object.keys(maps).length === 0) { installSourceMapSupport(); } maps[filename] = result.map; } return result.code; } //hook中傳入ext配置 function hookExtensions(exts) { if (piratesRevert) piratesRevert(); piratesRevert = addHook(compile, { exts, ignoreNodeModules: false }); } //入口函數 export default function register(opts?: Object = {}) { // Clone to avoid mutating the arguments object with the 'delete's below. opts = Object.assign({}, opts); if (opts.extensions) hookExtensions(opts.extensions); if (opts.cache === false && cache) { registerCache.clear(); cache = null; } else if (opts.cache !== false && !cache) { registerCache.load(); cache = registerCache.get(); } ... }
提供基礎的transform方法json
babel-plugin實際上是對code轉出的ast進行操做,segmentfault
ast的解構能夠類比成一個樹狀或者json嵌套結構,他的每一層結構均可以叫作一個節點,以下圖
babel提供一個visitor的方法,容許咱們在裏面指定咱們想要訪問的節點,而且能夠在命中該節點時作出自定義的的操做
如今咱們有一個須要移除整個業務bundle包裏全部console.log的需求
1.那咱們首先要知道console.log實際在ast是怎樣的一個節點結構
形如
console.log('a')
實際ast的展示以下
對於各個節點具體含義,這裏不作細講,能夠參考文末的babel手冊
2.這裏直接貼上代碼講吧
module.exports = function (babel) { const { types: t, template } = babel; const visitor = { //須要訪問的節點名 //訪問器默認會被注入兩個參數 path(類比成dom),state ExpressionStatement(path, state) { const node = path.node; //延當前節點向內部訪問,判斷是否符合console解析出的ast的特徵 const expressionNode = keyPathVisitor(node, ['expression']); const isCallExpression = expressionNode.type === 'CallExpression'; if (isCallExpression) { const objectName = keyPathVisitor(expressionNode, ['callee', 'object', 'name']); const prototypeName = keyPathVisitor(expressionNode, ['callee', 'property', 'name']); if (objectName === 'console' && prototypeName === 'log' && !MAC) { //若是符合上述條件,直接移除該節點 path.remove(); } } } }; return { visitor }; };
3.進階版:若是咱們想在babel-plugin中新增代碼呢
差很少有三種方法
A:手動添加節點(很噁心~相信你不會想去了解)
B:先生成ast,直接path.insertBefore
C:使用babel-template
例子: 移除autobind裝飾器,並在constructor中自動bind this
1.由於babel判斷是否babel是根據modify time,因此babel插件寫完想實時生效,須要給當前的env加上 BABEL_DISABLE_CACHE
//babel-register/cache.js function load() { if (process.env.BABEL_DISABLE_CACHE) return; process.on("exit", save); process.nextTick(save); if (!_fs2.default.existsSync(FILENAME)) return; try { data = JSON.parse(_fs2.default.readFileSync(FILENAME)); } catch (err) { return; } }
2.babel插件寫完後發佈npm時,記得必定要加上babel-plugin-前綴,由於配置在babelrc中的插件名都會被babel在加載時統一加上babel-plugin前綴,而後在模塊系統中去查找