Babel 社區概覽

本文發佈於 2019-04-15,總結了 babel 社區的工具使用,以及如何合理地進行配置。若是要看結論的話,直接跳到文章最後一節。javascript

目錄:

@babel/preset-env

介紹

babel-preset-env 是一系列插件的合集,官方已不用再使用 preset-201x 和 preset-latst 之類的包,env 包能夠根據配置自動處理兼容代碼。html

文檔:babeljs.io/docs/en/bab…java

targets

string | Array<string> | { [string]: string }, defaults to {}node

針對你的項目指定生成的代碼環境。git

能夠是字符串:es6

{
  "targets": "> 0.25%, not dead"
}
複製代碼

也能夠是對象:github

{
  "targets": {
    "chrome": "58",
    "ie": "11"
  }
}
複製代碼

默認是{}chrome

{
  "targets": {}
}
複製代碼

其中的環境值能夠參考 browserslist 項目。express

targets.esmodulesnpm

boolean

也能夠針對那些支持 ES Module 的瀏覽器而優化。當指定本選項時,browsers 字段會被忽視。你能夠和 <script type="module"></script> 一塊兒使用,來生成更小的腳本代碼。

請注意: 當指定 esmodules 選項, browsers targets 會被忽視。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "esmodules": true
        }
      }
    ]
  ]
}
複製代碼

targets.node

string | "current" | true

若是要針對當前 node 版本進行編譯,能夠指定 "node" :true"node":"current",它與 "node":process.versions.node 相同。

targets.safari

string | "tp"

若是要針對 Safari 的技術預覽版進行編譯,能夠指定「safari」:「tp」。

targets.browsers(廢棄)

string | Array<string>

使用 browserslist 選擇瀏覽器的查詢(例如:last 2 versions, > 5%, safari tp)。

注意,browsers 的結果會被 targets 中的顯式項覆蓋。(此特性通過檢驗已廢棄)

注意:這將在更高版本中刪除,而不是直接將 targets 設置爲 browserslist 的兼容查詢。

{
  "targets": {
    "browsers": {
        "chrome": "58",
        "ie": "11"
    }
  }
}
// 等價於,但最新版已經不能用了
{
  "targets": {
    "chrome": "58",
    "ie": "11"
  }
}
複製代碼

spec

boolean, 默認 false

爲此預設中支持它們的任何插件啓用更符合規範但可能更慢的轉換。

loose

boolean, 默認 false

爲此預設中容許它們的任何插件啓用 "loose" 轉換。

modules

"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false, defaults to "auto"

啓用將 ES6 模塊語法轉換爲其餘模塊類型。

將此設置爲 false 將不會轉換模塊。

也要注意 cjscommonjs 的別名。

debug

boolean, 默認 false

經過 console.log 輸出使用的 targets/plugins 和插件數據中指定的版本信息。

include

Array<string|RegExp>,默認 []

一個老是包含的插件數組。

如下是有效的配置:

  • Babel 插件@babel/plugin-transform-spread 和不帶前綴的 plugin-transform-spread 的寫法都支持。
  • 內置特性,好比 es6.mapes6.set,或者 es6.object.assign

能夠徹底或部分指定插件名稱(或使用 RegExp )。

支持的寫法:

  • 全稱(string): "es6.math.sign"
  • 部分 (string): "es6.math.*" (解析爲全部帶 es6.math 前綴的插件)
  • 正則對象: /^transform-.*$/ 或者 new RegExp("^transform-modules-.*")

注意,上面的正則對象中的 . 意思是匹配任何字符,而不是實際的 . 字符。 另請注意,匹配任何字符。.* 是在正則中使用,不一樣於 *glob格式中使用。

此選項主要針對於原生加強代碼中的 BUG,或者一系列沒有起做用的不受支持的功能特性。

例如,node 4 支持原生 class 但不支持 spread。若是 super 須要 spread 特性,那麼須要包含 @babel/plugin-transform-classes 插件。

注意:includeexclude 選項僅適用於此預設中包含的插件; 所以,在選項中包含 @babel/plugin-proposal-do-expressions 排除或 @babel/plugin-proposal-function-bind 會拋出錯誤(由於此預設沒有這些項目)。要使用此預設中未包含的插件,請直接將其添加到 plugin 選項中。

exclude

Array<string|RegExp>,默認 []

一個老是排除/移除的插件數組。

配置選項與 include 相同。

若是您不想使用 generators 而且不想包含 regeneratorRuntime(使用 useBuiltIns 時),或者使用了其餘插件(如 fast-async)而不是 Babel's async-to-gen,則此選項能夠將 @babel/plugin-transform-regenerator 等轉換禁用。

useBuiltIns

"usage" | "entry" | false, 默認是 false

此選項將 core-js 模塊直接引用爲裸導入。所以,core-js 將相對於文件自己進行解析,而且是可被訪問的。若是沒有 core-js 依賴項或者有多個版本,您可能須要將 core-js@2 指定爲應用程序中的頂級依賴項。

這個選項配置了 @babel/preset-env 如何處理 polyfills。

useBuiltIns: 'entry'

注意:只須要在你整個 app 中使用 require("@babel/polyfill"); 一次。屢次對 @babel/polyfill 的導入會致使全局衝突和其餘很難跟蹤的錯誤。咱們推薦建立一個單獨的文件處理 require 語句。

這個選項會啓用一個新的插件,將 import "@babel/polyfill" 或者 require("@babel/polyfill") 替換爲 @babel/polyfill 下的各個基於不一樣環境的單獨項導入。

npm install @babel/polyfill --save
複製代碼

輸入

import "@babel/polyfill";
複製代碼

輸出(不一樣的配置環境下有所區別)

import "core-js/modules/es7.string.pad-start";
import "core-js/modules/es7.string.pad-end";
複製代碼

也能夠直接導入 core-jsimport "core-js"; or require('core-js');

useBuiltIns: 'usage'(實驗性)

在每一個文件中使用 polyfill 時,爲 polyfill 添加特定導入。咱們利用 bundler 只加載一次相同的 polyfill。

輸入

a.js

var a = new Promise();
複製代碼

b.js

var b = new Map();
複製代碼

輸出(若是當前配置環境不支持此特性)

import "core-js/modules/es6.promise";
var a = new Promise();
複製代碼
import "core-js/modules/es6.map";
var b = new Map();
複製代碼

輸出(若是當前配置環境支持此特性)

var a = new Promise();
複製代碼
var b = new Map();
複製代碼

useBuiltIns: false

既不會在每一個文件中自動添加 polyfill,也不會將 "@babel/polyfill" 導入爲單個 polyfill。

簡單總結

'usage''entry' 的區別:

  • 'usage' 無需在頭部引入 import '@babel/polyfill',它會自動根據當前的代碼引入對應特性,而且只引用代碼中用到的特性(browserslist 配置 + 代碼用到)
  • 'entry' 須要在頭部引入 '@babel/polyfill',而且是根據配置環境引入對應的特性。代碼中沒有用到,但環境中會缺失,也會引入。(只根據 browserslist 配置)

usage 風險項:因爲咱們一般會使用不少 npm 的 dependencies 包來進行業務開發,babel 默認是不會檢測 依賴包的代碼的。 也就是說,若是某個 依賴包使用了 Array.from, 可是本身的業務代碼沒有使用到該API,構建出來的 polyfill 也不會有 Array.from, 如此一來,可能會在某些使用低版本瀏覽器的用戶出現 BUG。 因此避免這種狀況發生,通常開源的第三方庫發佈上線的時候都是轉換成 ES5 的。

corejs

corejs 配置項是決定當前 Babel 使用的版本,有 23 選項。

升級文檔中已經說明了,最新版的 Babel7 @babel/polyfill 移除了 polyfill proposals,因此 @babel/polyfill 僅僅是 core-js v2 的別名。

因此這裏就須要注意的一點,若是使用 corejs: 2 + useBuiltIns: 'entry' 的話,就會報警告:

`@babel/polyfill` is deprecated. Please, use required parts of `core-js` and `regenerator-runtime/runtime` separately
複製代碼

這裏須要使用的是 corejs: 3 + useBuiltIns: 'entry',纔不會出錯。

forceAllTransforms

boolean, 默認 false

因爲有了 Babel7 Javascipt config file 的支持,你能夠根據是否設置了 production 來控制轉換。

module.exports = function(api) {
  return {
    presets: [
      [
        "@babel/preset-env",
        {
          targets: {
            chrome: 59,
            edge: 13,
            firefox: 50,
          },
          // for uglifyjs...
          forceAllTransforms: api.env("production"),
        },
      ],
    ],
  };
};
複製代碼

注意: targets.uglify 已被廢棄,而且在下一個版本中被移除。

默認狀況下,此預設將運行目標環境所需的全部變換。若是你要強制運行全部轉換,則啓用此選項能夠在須要用到 UglifyJS 或僅支持 ES5 語法的某些場景下會頗有用。

configPath

string, 默認是 process.cwd()

決定配置 browserslist 搜索的起點,一直往上到系統根目錄,直到找到。

ignoreBrowserslistConfig

boolean, 默認是 false

切換是否使用 browserslist 配置源,包括搜索任何 browserslist 文件或引用package.json 中的 browserslist 鍵。這對於那些不走 Babel 編譯,但使用 browserslist 配置的項目很是有用。

shippedProposals

boolean, 默認是 false

切換啓用對瀏覽器中提供的內置特性的支持。若是你的目標環境對某一個特性提案(proposal)具備原生支持,則會啓用與其匹配的解析器語法插件,而不是執行任何轉換。請注意,這不會啓用與 @babel/preset-stage-3 相同的轉換,由於這些提案可能在正式落地瀏覽器以前會有變動。

目前支持如下內容:

內置:

特性:

@babel/preset-stage-x(廢棄)

在 babel7 中,官方已經宣佈廢棄 babel stage preset 包,大概是考慮到普遍使用的 stage-x 不適合社區的發展,具體緣由見官方博客

在新版的 babel 配置須要根據本身的須要下載對應的 proposal 插件,由於 stage-x 自己也是這些插件的集合,但不包含在 env 包中,好比安裝:@babel/plugin-proposal-function-bind,使用這些還沒正式進標準但社區已經廣爲使用的語言特性。

具體使用的話,之前的各個 stage 等同於下面的各個插件的集合:

{
  "plugins": [
    // Stage 0
    "@babel/plugin-proposal-function-bind",

    // Stage 1
    "@babel/plugin-proposal-export-default-from",
    "@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",

    // Stage 2
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",

    // Stage 3
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    ["@babel/plugin-proposal-class-properties", { "loose": false }],
    "@babel/plugin-proposal-json-strings"
  ]
}
複製代碼

@babel/polyfill

介紹

文檔:babeljs.io/docs/en/nex…

官網信息:從 Babel 7.4.0 開始,這個包已經被廢棄了,以支持直接導入 core-js/stable(polyfill ECMAScript 特性)和 regenerator-runtime/runtime(須要使用轉換後的 generator 函數)

babel-polyfill 的存在乎義是給瀏覽器「打補丁」,好比瀏覽器沒有 Object.assign 這個特性,它會針對這個環境建立這個特性。Babel 自身是隻轉換語法,不添加丟失的特性,polyfill 的存在就是彌補瀏覽器這部分缺失的特性(好比某些 ie)。

babel-polyfill 等同於 regenerator runtime + core-js

  • regenerator:提供對 generator 支持,若是應用代碼中用到generator、async函數的話。
  • core-js:提供 es 新的特性。

最新版的具體用法,見 @babel/preset-envuseBuiltIns 特性。

通過個人簡單實驗,其實能夠不用專門安裝這個包,並且新的 corejs v3 和 corejs v2 還不太同樣(使人困惑)。使用 useBuiltIns` 就好。

反作用

引入 babel-polyfill 也會有必定反作用,好比:

  • 引入了新的全局對象,好比Promise、WeakMap等。
  • 修改現有的全局對象:好比修改了Array、String的原型鏈等。

在應用開發中,上述行爲問題不大,基本可控。但若是在庫、工具的開發中引入 babel-polyfill,則會帶來潛在的問題。

舉個例子,在項目中定義了跟規範不一致的Array.from()函數,同時引入了一個庫(依賴 babel-polyfill),此時,這個庫可能覆蓋了自定義的Array.from()函數,致使出錯。

這就是 babel-runtime 存在的緣由。它將開發者依賴的全局內置對象等,抽取成單獨的模塊,並經過模塊導入的方式引入,避免了對全局做用域的修改(污染)。

所以,若是是開發庫、工具,能夠考慮使用 babel-runtime。

@babel/runtime

介紹

@babel/runtime 是一個包含 Babel modular runtime helpers 和 一系列 regenerator-runtime 的庫。

文檔:babeljs.io/docs/en/bab…

使用場景

babel-runtime 通常用於兩種場景:

  • 開發庫/工具
  • 移除冗餘工具函數(helper function)。

與 babel-polyfill 的區別在於:

  • babel-polyfill 會修改(覆蓋)實例方法,這在業務層頗有用,但某些場景,好比引用外在的技術庫,不但願這裏的的 polyfill 覆蓋業務代碼中的方法。
  • babel-runtime 不會修改實例方法,它只是引入一些 helper 函數,創造對應的方法。

使用 babel-runtime 通常會搭配 babel-plugin-transform-runtime 使用。babel-plugin-transform-runtime 用於構建過程的代碼轉換,而 babel-runtime 是實際導入項目代碼的功能模塊。

@babel/plugin-transform-runtime

介紹

文檔:babeljs.io/docs/en/bab…

babel 在每一個須要的文件的頂部都會插入一些 helpers 內聯代碼,這可能會致使多個文件都會有重複的 helpers 代碼。@babel/plugin-transform-runtime 的 helpers 選項就能夠把這些模塊抽離出來。

@babel/plugin-transform-runtime 主要作了三件事情:core-js aliasing、helper aliasing、egenerator aliasing。

  • core-js aliasing:自動導入babel-runtime/core-js,並將全局靜態方法、全局內置對象 映射到對應的模塊。

  • helper aliasing:將內聯的工具函數移除,改爲經過babel-runtime/helpers模塊進行導入,好比_classCallCheck工具函數。

  • regenerator aliasing:若是你使用了 async/generator 函數,則自動導入 babel-runtime/regenerator模塊。

Demo

module.exports = {
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": false, // boolean 或者 number, 默認 false,指定是否須要 runtime 的 corejs aliasing,若是使用 env 的 useBuiltIns + polyfill,使用 false。
                "helpers": true, // boolean, 默認 true,指定是否內聯 babel 的 helper 代碼 (好比 classCallCheck, extends) 
                "regenerator": false, // 經過 preset-env 已經使用了全局的 regeneratorRuntime, 再也不須要 transform-runtime 提供的 不污染全局的 regeneratorRuntime
                "useESModules": true, // boolean, 默認 false,使用 es modules helpers, 減小 commonJS 語法代碼
                "absoluteRuntime": false // boolean, 默認 false,是否目錄引用 runtime 包(有些項目會引用當前項目以外的代碼,編譯時會找不到 runtime 包)
            }
        ]
    ]
}
複製代碼

添加新配置前編譯出來的代碼

import "core-js/modules/es6.promise";
import "regenerator-runtime/runtime";

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
複製代碼

添加新配置後編譯出來的代碼

import "core-js/modules/es6.promise";
import "regenerator-runtime/runtime";
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
複製代碼

babel-register

babel-register 則提供了動態編譯。換句話說,咱們的源代碼可以真正運行在生產環境下,不須要 babel 編譯這一環節。

咱們先在項目下安裝 babel-register

$ npm install --save-dev @babel/register
複製代碼

而後在入口文件中 require

require('@babel/register')
require('./app')
複製代碼

在入口文件頭部引入 @babel/register 後,咱們的 app 文件中便可使用任意 es2015 的特性。

固然,壞處是動態編譯,致使程序在速度、性能上有所損耗。因此這一項基本不用在正式的生產環境中使用。

babel-node

上面所說,babel-register 提供動態編譯,可以讓咱們的源代碼真正運行在生產環境下 - 但其實否則,咱們仍須要作部分調整,好比新增一個入口文件,並在該文件中 require('@babel/register')。而 babel-node 能真正作到一行源代碼都不須要調整:

$ npm install --save-dev @babel/core @babel/node
$ npx babel-node app.js
複製代碼

只是,請不要在生產環境中使用 babel-node,由於它是動態編譯源代碼,應用啓動速度很是慢。

一份可用的配置

安裝對應的包

依賴:

  • @babel/core(核心包)
  • @babel/preset-env(預設)
  • @babel/polyfill(v7.4.0彷佛被廢棄,能夠不用安裝)
  • core-js(最新版本v3,在配置版本corejs:3的狀況下,這個包是用於替代 polyfill 的)
  • @babel/runtime(開發業務代碼基本只用到helper配置,開發技術庫能夠深刻使用)
  • @babel/plugin-transform-runtime(合併重複的 helper 函數)
  • @babel/plugin-proposal-function-bind(沒有 stage-x 後,須要安裝單獨的插件,支持對應的 proposal 特性)

注意:這裏 @babel/polyfill 可裝可不裝,不裝彷佛也不影響沒有影響,但不肯定正式允運行的時候會不會報錯。看了下源碼,其實很簡單,就是引用到 core-js v2 的特性。官方文檔介紹已經被廢棄了。

babel.config.js

const presets = [
    [
      "@babel/env",
      {
        targets: {
          edge: "17",
          firefox: "60",
          chrome: "67",
          safari: "11.1",
          ie: '8'
        },
        useBuiltIns: 'usage',
        // Babel7 須要指定引入corejs的版本,最好使用3
        corejs: 3,
        modules: 'amd', // 須要轉換成什麼樣的模塊系統
      },
    ],
  ];
  
const plugins = [
    // 幫助減小 helper 函數
    [
      "@babel/plugin-transform-runtime",
      {
          "corejs": false, // 默認值,能夠不寫
          "helpers": true, // 默認,能夠不寫
          "regenerator": false, // 經過 preset-env 已經使用了全局的 regeneratorRuntime, 再也不須要 transform-runtime 提供的 不污染全局的 regeneratorRuntime
          "useESModules": true, // 使用 es modules helpers, 減小 commonJS 語法代碼
      }
    ],
    // 因爲沒有了 stage-x,須要單獨導入須要的插件
    [
        '@babel/plugin-proposal-function-bind'
    ]
]
module.exports = { presets, plugins };
複製代碼

參考

Babel 的使用

Babel 的配置信息

相關文章
相關標籤/搜索