最近在做自動國際化,用到了babel
的外掛, 所以爲了減小自動國際化的篇幅,就把這部分單獨提出來,目的是爲了能讓小白都能看懂babel
外掛的真實面目。
babel外掛
babel的編譯流程:
主要分為三步,流程主要為:
parsing(解析)、transforming(轉化)、generating(生成)
parsing:
它使用@babel/parser
庫, 負責將es6
程式碼進行語法分析和詞法分析後轉換成抽象語法樹AST
transforming:
plugin
外掛利用@babel/traverse
庫來,來遍歷AST
節點,操作他們,用它提供的API
來編寫對AST
的遍歷和修改邏輯,由此來將一種AST
轉換為另一種AST
generating:``@babel/generator
負責將處理後的AST樹
生成新的 es5 程式碼
Plugins 和 Presets 的執行順序
Plugins 在 Presets 前執行
Plugins 順序從前往後排列
Presets 順序是顛倒的(從後往前)
推薦一個寫外掛好用的AST語法樹結構工具,在寫外掛的時候,你不要知道他的type到底是什麼,就可以用這個工具幫你查詢。
babel編譯檔案
引入babel:
npm install --save-dev @babel/cli
在src/index.js檔案裡面寫一個函式
const fn = () => { console.log('hello babel') }
3.用babel編譯 執行命令
npx babel src --out-dir lib
結果
我想編譯某一個檔案就寫這樣:
npx babel src/index.js --out-dir lib npx babel src/app.js --out-dir lib
在lib下面就會出現編譯後的具體的檔案。
babel外掛實現
現在我們寫一個外掛,希望把具體index.js
裡面 ceshi
的函式名改成test
,把你的函式copy
到 AST Explorer裡面,然後點一下 ceshi
函式名,就會出現對應的 AST
結構。
其實babel
外掛就是一個函式,把這個函式接入到babel
的配置檔案中,它就會自動執行。所以在plugin/index.js
檔案裡面匯出一個函式就好了。
在return裡面匯出一個編譯器物件,它包含一個visitor
屬性,它裡面包含了我們要操作的內容,比如在上圖,我們看到函式名的結構是:Identifier
, 那麼咱們的外掛就變成了這樣的,順便在控制檯列印下path.name
看看
建立babel.config.js
檔案,引入外掛
執行命令:
npx babel src/index.js --out-dir lib
發現
寫一下外掛業務
module.exports = function () { return { visitor: { Identifier(path) { let name = path.node.name; if (name === 'ceshi') { console.log(path.node) path.node.name = 'test' } }, }, }; }
執行npx babel src/index.js --out-dir lib
檢視結果:
你看一個小小的babel外掛就搞定了,我想打包釋出到npm上,然後在其他包裡面使用怎麼辦?
babel外掛釋出npm
npm login
輸入使用者名稱和密碼,報錯code 403
因為源地址應該是淘寶的,所以安裝 npm 管理器:nrm,之前有個nvm是node版本管理器,現在出來個nrm,專門管理npm的源地址:
npm i nrm -g nrm ls nrm use npm
你要想看看現在用的是哪個映象源,就執行下nrm ls
,或者 npm config get registry
現在再登陸
npm login
在釋出之前,一定要建立一個檔案是.npmignore
在這個檔案裡面寫上你不需要釋出的檔案比如:
在釋出之前,要對程式碼進行打包,在npm裡面的檔案一般都是處理以後的檔案,這樣publish的時候體積比較小,使用的時候才快。一般工具庫打包都用的rollup,一般應用型專案纔會用webpack和vite。
npm i rollup -D
建立配置檔案:
在package.json
的scripts
裡配置命令:
執行打包
npm run build
執行釋出
npm publish
它會把你專案下面所有的東西都發布到npm上去,在釋出過程中,我發現我的專案名字和別人的重複了,釋出不上去,我就改了專案要名叫:babel-plugin-change-name1
備註:在打包的時候,請一定要將程式碼統一掉,要麼全部是ESM 的,要麼是commonjs的。我的專案採用的ESM,在package.json裡面配置type:module就好了。當然 rollup 預設支援ESM,你要是想要打包commonJS的就要用外掛了。所以沒有必要這麼做。我們只需要將工具包和使用包全部預設是ESM 就不會出現不必要的bug。
檢查發現工具程式碼裡面有commonJS的匯出,所以趕緊改改,再次打包,釋出即可。
測試babel外掛
在另外一個專案裡面安裝測試下看看
npm i babel-plugin-change-name1 -D
執行babel編譯命令報錯
說明這個vite應用預設支援commonJS,需要在package.json裡面配置
把babel外掛裡面的測試檔案拿過來,然後再咱們的專案裡面執行npx babel src/index.js --out-dir lib
檢視結果:
終於把ceshi 改成了 test 是不是?
很明顯這樣不合理,我們一般都是執行打包的時候,babel纔去執行的,現在是用一個命令在執行,我們想辦法把他配到webpack或者vite裡面去,當執行npm run build
的時候,babel
自動處理程式碼。
babel接入打包工具webpack、vite
vite整合babel
安裝 pnpm i vite-plugin-babel @babel/core -D
然後進入
vite.config.ts
檔案中使用babel外掛,並且將build.target
設定為es2015
即可
import vue from '@vitejs/plugin-vue' import babel from "vite-plugin-babel"; import vueJsx from '@vitejs/plugin-vue-jsx' export default defineConfig({ base: './', plugins: [ babel(), vue(), vueJsx(), // 其他外掛... ], build: { target: 'es2015' } })
webpack5 整合babel
安裝
npm install -D babel-loader @babel/core @babel/preset-env
webpack.config.js
配置
module: { rules: [ { test: /\.?js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } } ] }
babel.config.json
配置
{ "presets": [ ["@babel/preset-env", { "targets": "> 0.25%, not dead" }] ], "plugins": [ // 不汙染全域性,在執行時載入 ["@babel/plugin-transform-runtime", { "corejs": 3 }] ] }
接入我們的專案
我們只需要引入babel-loader
就好了,具體babel的配置放到babel.config.js
裡面,這樣做的好處是webpack
的配置檔案不會龐大而繁雜,難以閱讀和修改。
執行打包
npm run dev
發現報錯了,只要 package.json
檔案裡面寫 type:module
那麼所有的檔案都要用 ESM
改完以後,打包正常
npm run build
此時你發現他成功了,但是你用index.html引入測試下看看
開啟頁面
因為打包的時候用了sourcemap,我們對應的是本地原始碼,他已經幫我們略過了中間環節,測試的時候,用npx babel src/index.js --out-dir lib
測試透過,就沒有問題。
整個自定義babel外掛流水線全部結束,希望您能和我一樣有所收穫。
總結:
外掛名稱必須是babel-plugin-xxx
.babelrc可以新增你的外掛以及配置引數
外掛編寫必須遵循規範
當你想改變某個節點時,使用該節點名(首字母大寫)稱作為函式名進行訪問,使用path.replaceWith或者repalceWithMultiple替換