摘要:是怎么工作的如何編譯應(yīng)用場(chǎng)景語(yǔ)法糖的代碼統(tǒng)一相關(guān)概念介紹依賴,提供的方法,只轉(zhuǎn)化語(yǔ)法,不轉(zhuǎn)換類,的基礎(chǔ)配置利用對(duì)進(jìn)行劫持,在中進(jìn)行原理見(jiàn)同時(shí)對(duì)后的進(jìn)行緩存,提高下次效率讀取緩存根據(jù)判斷是否需要重新中傳入配置入口函數(shù)提供
Babel babel是怎么工作的?
parse->AST->transform->gengerate
如何編譯js->AST
babel應(yīng)用場(chǎng)景語(yǔ)法糖的polyfill
代碼統(tǒng)一hack
相關(guān)概念介紹 babel-polyfill依賴core-js,提供es*->es3的方法,只轉(zhuǎn)化語(yǔ)法,不轉(zhuǎn)換API(類Promise,WeakMap)
babel-helper babel-register(0.7.0-beta)babel的基礎(chǔ)配置init
利用pirate對(duì)require進(jìn)行劫持,在hook中進(jìn)行babel 原理見(jiàn)
同時(shí)對(duì)babel后的code進(jìn)行緩存,提高下次babel效率
function compile(code, filename) { ... let cacheKey = `${JSON.stringify(opts)}:${babel.version}`; const env = babel.getEnv(false); if (env) cacheKey += `:${env}`; //讀取緩存 根據(jù)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 }); } //入口函數(shù) 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(); } ... }babel-core
提供基礎(chǔ)的transform方法
如何寫一個(gè)babel插件babel-plugin其實(shí)是對(duì)code轉(zhuǎn)出的ast進(jìn)行操作,
準(zhǔn)備工具ast轉(zhuǎn)換工具
ast轉(zhuǎn)換可視化工具
ast的解構(gòu)可以類比成一個(gè)樹狀或者json嵌套結(jié)構(gòu),他的每一層結(jié)構(gòu)都可以叫做一個(gè)節(jié)點(diǎn),如下圖
babel提供一個(gè)visitor的方法,允許我們?cè)诶锩嬷付ㄎ覀兿胍L問(wèn)的節(jié)點(diǎn),并且可以在命中該節(jié)點(diǎn)時(shí)做出自定義的的操作
實(shí)例分析現(xiàn)在我們有一個(gè)需要移除整個(gè)業(yè)務(wù)bundle包里所有console.log的需求
1.那我們首先要知道console.log實(shí)際在ast是怎樣的一個(gè)節(jié)點(diǎn)結(jié)構(gòu)
形如
console.log("a")
實(shí)際ast的展現(xiàn)如下
對(duì)于各個(gè)節(jié)點(diǎn)具體含義,這里不做細(xì)講,可以參考文末的babel手冊(cè)
2.這里直接貼上代碼講吧
module.exports = function (babel) { const { types: t, template } = babel; const visitor = { //需要訪問(wèn)的節(jié)點(diǎn)名 //訪問(wèn)器默認(rèn)會(huì)被注入兩個(gè)參數(shù) path(類比成dom),state ExpressionStatement(path, state) { const node = path.node; //延當(dāng)前節(jié)點(diǎn)向內(nèi)部訪問(wèn),判斷是否符合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) { //如果符合上述條件,直接移除該節(jié)點(diǎn) path.remove(); } } } }; return { visitor }; };
3.進(jìn)階版:如果我們想在babel-plugin中新增代碼呢
差不多有三種方法
A:手動(dòng)添加節(jié)點(diǎn)(很惡心~相信你不會(huì)想去了解)
B:先生成ast,直接path.insertBefore
C:使用babel-template
例子: 移除autobind裝飾器,并在constructor中自動(dòng)bind this
1.因?yàn)閎abel判斷是否babel是根據(jù)modify time,所以babel插件寫完想實(shí)時(shí)生效,需要給當(dāng)前的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插件寫完后發(fā)布npm時(shí),記得一定要加上babel-plugin-前綴,因?yàn)榕渲迷赽abelrc中的插件名都會(huì)被babel在加載時(shí)統(tǒng)一加上babel-plugin前綴,然后在模塊系統(tǒng)中去查找
題外話 如何實(shí)現(xiàn)給require加上hook傳送門
參考文獻(xiàn)Babel插件手冊(cè)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/93618.html
摘要:抽象語(yǔ)法樹是怎么生成的談到這點(diǎn),就要說(shuō)到計(jì)算機(jī)是怎么讀懂我們的代碼的。需要注意什么狀態(tài)狀態(tài)是抽象語(yǔ)法樹轉(zhuǎn)換的敵人,狀態(tài)管理會(huì)不斷牽扯我們的精力,而且?guī)缀跛心銓?duì)狀態(tài)的假設(shè),總是會(huì)有一些未考慮到的語(yǔ)法最終證明你的假設(shè)是錯(cuò)誤的。 現(xiàn)在談到 babel 肯定大家都不會(huì)感覺(jué)到陌生,雖然日常開發(fā)中很少會(huì)直接接觸到它,但它已然成為了前端開發(fā)中不可或缺的工具,不僅可以讓開發(fā)者可以立即使用 ES 規(guī)范...
摘要:是一個(gè)對(duì)象,它表示兩個(gè)節(jié)點(diǎn)之間的連接。接著返回一個(gè)對(duì)象,其屬性是這個(gè)插件的主要節(jié)點(diǎn)訪問(wèn)者。所以上面的執(zhí)行方式是運(yùn)行引入了自定義插件的打包文件現(xiàn)在為明顯減小,自定義插件成功插件文件目錄覺(jué)得好玩就關(guān)注一下歡迎大家收藏寫評(píng)論 目錄 Babel簡(jiǎn)介 Babel運(yùn)行原理 AST解析 AST轉(zhuǎn)換 寫一個(gè)Babel插件 Babel簡(jiǎn)介 Babel 是一個(gè) JavaScript 編譯器,它能將es...
摘要:我寫過(guò)一些開源項(xiàng)目,在開源方面有一些經(jīng)驗(yàn),最近開到了阮老師的微博,深有感觸,現(xiàn)在一個(gè)開源項(xiàng)目涉及的東西確實(shí)挺多的,特別是對(duì)于新手來(lái)說(shuō)非常不友好最近我寫了一個(gè),旨在從多方面快速幫大家搭建一個(gè)標(biāo)準(zhǔn)的庫(kù),本文將已為例,介紹寫一個(gè)開源庫(kù)的知識(shí) 我寫過(guò)一些開源項(xiàng)目,在開源方面有一些經(jīng)驗(yàn),最近開到了阮老師的微博,深有感觸,現(xiàn)在一個(gè)開源項(xiàng)目涉及的東西確實(shí)挺多的,特別是對(duì)于新手來(lái)說(shuō)非常不友好 show...
摘要:本文特此給大家介紹下如何使用配合來(lái)構(gòu)建基于的前端項(xiàng)目。最后,在目錄下會(huì)生成最終的項(xiàng)目文件。執(zhí)行單元測(cè)試本例中使用進(jìn)行單元測(cè)試。 隨著React、Angular2、Redux等前沿的前端框架越來(lái)越流行,使用webpack、gulp等工具構(gòu)建前端自動(dòng)化項(xiàng)目也隨之變得越來(lái)越重要。鑒于目前業(yè)界普遍更流行使用webpack來(lái)構(gòu)建es6(ECMAScript 2015)前端項(xiàng)目,網(wǎng)上的相關(guān)教程也比...
摘要:踩坑入門系列一二添加三目錄重構(gòu)再談路由陸續(xù)更新個(gè)人對(duì)于腳手架的有一種執(zhí)念,如果搭建出來(lái)就是一個(gè)首頁(yè)標(biāo)簽跳轉(zhuǎn),實(shí)在不是我這個(gè)處女座的風(fēng)格,因此第二步我就想引用框架,相信很多使用的開發(fā)者用的也都是這個(gè)框架吧。 Next.js踩坑入門系列 (一) Hello Next.js (二) 添加Antd && CSS (三) 目錄重構(gòu)&&再談路由 陸續(xù)更新... 個(gè)人對(duì)于腳手架的UI有一種執(zhí)...
閱讀 2965·2021-11-23 10:12
閱讀 2690·2021-11-23 09:51
閱讀 2040·2021-11-15 11:37
閱讀 1352·2019-08-30 15:55
閱讀 1965·2019-08-29 15:40
閱讀 1165·2019-08-28 18:30
閱讀 1650·2019-08-28 18:02
閱讀 2640·2019-08-26 12:00