Babel 7

Babel-7

babel是一個工具鏈,其主要用於將ES5以上對的版本代碼轉化爲向下兼容的JavaScript語法。

本文是針對babel7.10.0講解的

創建測試項目

mkdir babel-test
cd babel-test
npm init
  • babel-test根目錄下創建.babelrc文件,用於配置babel。

    babel命令會從當前編譯文件依次向上查找,直到找到.babelrcbabel.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.jstarget-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

詳情請參考:https://www.babeljs.cn/docs/plugins

@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/polyfillcore-js2regenerator-runtime組成,用來實現對generatorasyncPromiseSymbol等函數的支持。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("異步成功");
});

可以看到自動引入了promiseincludesto-string三個api。

@babel/runtime和@babel/plugin-transform-runtime

@babel/runtime依賴於@babel/helpersregenerator-runtime

@babel/helpers:包含了很多輔助方法,可以便於將高階語法轉化爲地接語法,相當於公共函數類。例如class轉換_classCallCheck

regenerator-runtime:生成器函數、asyncawait經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語法樹,使其滿足自己的需求,然後重新生成新的代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章