Babel-7
babel是一個工具鏈,其主要用於將ES5以上對的版本代碼轉化爲向下兼容的JavaScript語法。
本文是針對babel7.10.0講解的
創建測試項目
mkdir babel-test
cd babel-test
npm init
-
在
babel-test
根目錄下創建.babelrc
文件,用於配置babel。babel命令會從當前編譯文件依次向上查找,直到找到
.babelrc
或babel.config.js
爲止。 -
在根目錄下新建
source-code
目錄用於存放源碼。 -
在根目錄下新建
target-code
目錄用於存放編譯後的代碼。
@babel/core
babl核心包,其定義了代碼轉換的一些方法,可將源代碼根據配置轉換爲兼容環境的目標代碼。
npm install --save-dev @babel/core
幾乎所有babel相關組件均依賴於它,沒有該包,則無法正常執行編譯操作。
@babel/cli
@babel/cli
是babel提供的內建命令工具,可以在終端使用命令主動編譯需要的文件。
npm i --save-dev @babel/cli
@babel/cli
是babel命令工具,不適合全局安裝。
@babel/node
是babel對動態編譯的支持,適合全局安裝,會損失程序速度和性能。他們都依賴於
@babel/core
在source-code
目錄下新建test.js
文件,內容如下:
const funA = ()=>{
let arr = ["a","b","c"];
let isExist = arr.includes((item)=>item=="c");
console.log(isExist)
}
funA();
new Promise((resolve)=>{
console.log("異步操作")
resolve();
}).then(()=>{
console.log("異步成功")
})
轉換命令
npx babel source-code/test.js --out-file target-code/out.js
將
source-code/test.js
編譯後,生成out.js
到target-code
目錄下。
此時你會發現out.js
生成了,但是未進行編譯轉換。因爲babel需要根據配置來確認需要轉換爲什麼環境的代碼,babel7將babel6中集成的編譯功能拆分爲了一個個插件,默認情況下無可用插件編譯,所以無法執行編譯。
@babel/plugin*
babel7是通過一個個插件來進行編譯的,如果沒有插件則無法進行編譯轉換。
例如:上面test.js中使用了=>
,箭頭函數的編譯插件爲:@babel/plugin-transform-arrow-functions
npm i --save-dev @babel/plugin-transform-arrow-functions
修改執行命令爲:
npx babel source-code/test.js --out-file target-code/out.js --plugins=@babel/plugin-transform-arrow-functions
此時你可以發現
=>
已經轉換爲了function()
。當然一段代碼的編譯將使用到很多的插件,例如:
- 將const,let轉換爲var:
@babel/plugin-transform-block-scoping
- 將class轉化爲function:
@babel/plugin-transform-classes
@babel/presets
presets
是插件集合,我們代碼中可能用到了多個需要轉換的插件,如果一個一個的引入,耗時且難以維護。所以官方提出了一些插件集合來簡化我們的引入操作。
常見的插件集有:
-
@babel/preset-env
:ES2015、ES2016、ES2017的語法插件集其不支持stage-x插件的轉換
-
@babel/preset-stage-0
:stage-0,1,2,3都是對預設語法的轉換插件,越靠前的插件,包含內容越多。stage-0
包含了1,2,3的所有插件,另外還支持transform-do-expressions
,transform-function-bind
。Babel7
開始廢棄了stage-x
的預設集,請謹慎使用,在babel7種按需導入對應的插件即可。 -
@babel/plugin-transform-flow-strip-types
:去除ts類型校驗 -
@babel/preset-react
:支持jsx語法的轉換 -
babel-preset-minify
:壓縮代碼 -
@babel/preset-typescript
:轉換ts代碼
npm install --save-dev @babel/preset-env
在.babelrc中添加如下信息:
{
"presets": [
"@babel/preset-env"
],
"plugins": []
}
執行編譯:
npx babel source-code/test.js --out-file target-code/out.js
可發現除了
Promise
,代碼基本被編譯了。
@babel/polyfill
@babel/polyfill
由core-js2
和regenerator-runtime
組成,用來實現對generator
、async
、Promise
、Symbol
等函數的支持。babel
默認進隊對js語法進行解析,並不能轉換API。
npm i --save-dev @babel/polyfill
在入口文件頭部引入@babel/polyfill
,即可按需加載文件中用到的api。
使用方法
修改.babelrc
配置
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage", //手動引入@babel/polyfill,如果設置爲entry則可在配置在entry上,自動引入
"corejs": "3" //指定corejs的版本,否則會有警告
}
]
],
"plugins": []
}
在test.js
頭部添加import "@babel/polyfill";
執行編譯後代碼如下:
"use strict";
require("core-js/modules/es.array.includes");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.promise");
var funA = function funA() {
var arr = ["a", "b", "c"];
var isExist = arr.includes(function (item) {
return item == "c";
});
console.log(isExist);
};
funA();
new Promise(function (resolve) {
console.log("異步操作");
resolve();
}).then(function () {
console.log("異步成功");
});
可以看到自動引入了
promise
、includes
、to-string
三個api。
@babel/runtime和@babel/plugin-transform-runtime
@babel/runtime
依賴於@babel/helpers
和regenerator-runtime
。
@babel/helpers
:包含了很多輔助方法,可以便於將高階語法轉化爲地接語法,相當於公共函數類。例如class轉換_classCallCheck
。
regenerator-runtime
:生成器函數、async
、await
經babel編譯後,regenerator-runtime
模塊用於提供功能實現。
@babel/runtime
解決了2個問題:
- 每個模塊編譯都自動引入了api類,但這會造成相同引用類多次複用而引入的文件體積變大。
@babel/runtime
抽離了公共方法,存放在@babel/helpers
中,減少了代碼體積。 @babel/polyfill
雖按需引入了,但是會污染全局命名空間,當用戶自定的方法和公共庫方法重名時,就會造成衝突。
npm i --save-dev @babel/plugin-transform-runtime
npm i --save @babel/runtime
npm install --save @babel/runtime-corejs2
@babel/runtime
需搭配@babel/plugin-transform-runtime
使用。
@babel/plugin-transform-runtime
的corejs默認爲false,即不對polyfill進行處理,可設置爲不同的版本讓其支持。
執行編輯後可以發現,對polyfill
引入內容添加了別名,這樣就避免了全局污染。
@babel/register
@babel/register
提供了動態編譯,使我們的源碼可以直接在生產環境運行,而不是先編譯後生成環境才能執行。
npm install --save-dev @babel/register
使用時,在入口文件頭部引入require("@babel/register");
,即可實現動態編譯,不建議使用,因爲損耗生成環境性能。
stage-x
Babel7.10.0
開始正式廢棄了預設提案stage-x
的支持需要我們手動按需在插件中依次引入。
"plugins": [
// Stage 0
"@babel/plugin-proposal-function-bind", //::修飾符轉化爲bind或者call
// Stage 1
"@babel/plugin-proposal-export-default-from", //export * form語法支持
"@babel/plugin-proposal-logical-assignment-operators", //||=,&&=等合併操作符的支持
["@babel/plugin-proposal-optional-chaining", { "loose": false }],//?.屬性名 鏈式結構的支持
["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }], //管道鏈接器|>的支持
["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }], //?? 操作符的支持
"@babel/plugin-proposal-do-expressions", //do{...} 表達式的支持
// Stage 2
["@babel/plugin-proposal-decorators", { "legacy": true }], //註解修飾器的支持
"@babel/plugin-proposal-function-sent", //function* generator函數別名的支持
"@babel/plugin-proposal-export-namespace-from", //export * as name form語法支持,即導出別名的支持
"@babel/plugin-proposal-numeric-separator", //**,>>,<<等數學運算符的支持
"@babel/plugin-proposal-throw-expressions", //throw new Error(),異常拋出函數的支持
// Stage 3
"@babel/plugin-syntax-dynamic-import", //按需導入的支持,目前可以用polyfill代替API的導入
"@babel/plugin-syntax-import-meta",
["@babel/plugin-proposal-class-properties", { "loose": false }],//class類解析
"@babel/plugin-proposal-json-strings" //字符串中添加固定字符
]
antd樣式按需加載
"plugins": [
"@babel/plugin-syntax-dynamic-import",
["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css"}, "ant"],
["import", { "libraryName": "antd-mobile", "libraryDirectory": "lib", "style": "css"},"antd-mobile"]
]
@babel/plugin-proposal-object-rest-spread
:支持{…a},解構語法,使用較多。
自動替換stage-x插件
npx babel-upgrade --write
運行該命令會自動更新
package.json
和.babelrc
文件,替換package.json中出現的stage-x
預設集。
自定義插件
babel
轉換分爲3個步驟:詞法解析,邏輯轉換,代碼生成。
一個插件就是一個函數,其接受一個babel對象,返回一個包含visitor
的對象,visitor
就是一顆AST語法樹。
新建source-code/my-plugin.js
,內容如下:
module.exports = function (babel) {
const t = babel.types;
return {
name: "myPlugin",
visitor: {
CallExpression(path) {
const obj = path.node.callee.object //對象
const prop = path.node.callee.property //屬性
const arguments = path.node.arguments //參數
if (t.isIdentifier(obj) && t.isIdentifier(prop) && obj.name === 'console' && prop.name === 'log') {
const location = `****location:row:${path.node.loc.start.line}, column:${path.node.loc.start.column}****`;
//爲每個console.log()語句的末尾加上一個位置信息
arguments.push(t.stringLiteral(location))
}
}
}
}
}
這裏我們定義了一個插件,爲每個console.log增加了一個參數,顯示打印位置。
使用
在.babelrc
中添加上訴插件:
"plugins": [
"./source-code/my-plugin.js"
]
執行編譯,可發現console.log("異步操作")
=>console.log("異步操作", "****location:row:10, column:4****")
自定義插件就是操作AST語法樹,使其滿足自己的需求,然後重新生成新的代碼。