關(guān)于Babel你只需要知道三個(gè)插件

文章總結(jié)的時(shí)間是2017/11/20

本文是為了梳理Babel配置及使用而整理,因?yàn)榭催^(guò)使用Babel配置項(xiàng)目和文章,存在項(xiàng)目插件使用混亂、文章各種照搬、插件使用聽(tīng)風(fēng)是雨、插件升級(jí)文章內(nèi)容不再適用的問(wèn)題。這里就目前最新使用的配置組合進(jìn)行整理,涉及的插件包括以下三個(gè):

  • @babel/preset-env(^7.0.0-beta.32)
  • @babel/preset-stage-x(7.0.0-beta.32), x-0,1,2,3
  • @babel/polyfill(^7.0.0-beta.32)

@babel/preset-env

特性

替換之前所有babel-presets-es20xx插件

A Babel preset that compiles ES2015+ down to ES5 by automatically determining the Babel plugins and polyfills you need based on your targeted browser or runtime environments.

也就是說(shuō),這是一個(gè)能根據(jù)運(yùn)行環(huán)境為代碼做相應(yīng)的編譯,@babel/preset-env的推出是為了解解決個(gè)性化輸出目標(biāo)代碼的問(wèn)題,通過(guò)browserslist語(yǔ)法解析需要支持的目標(biāo)環(huán)境,根據(jù)環(huán)境將源代碼轉(zhuǎn)義到目標(biāo)代碼,可以實(shí)現(xiàn)代碼精準(zhǔn)適配。

此外,@babel/preset-env不包含state-x一些列插件,只支持最新推出版本的JavaScript語(yǔ)法(state-4),關(guān)于state-x后面會(huì)介紹。

更進(jìn)一步說(shuō)明,請(qǐng)參考Dr. Axel Rauschmayer的介紹。這個(gè)插件對(duì)特殊平臺(tái)的開(kāi)發(fā)有很大幫助,比如:Electron、大屏、移動(dòng)端(只考慮webkit)等。

替換@babel/plugin-transform-runtime的使用

@babel/plugin-transform-runtime插件是為了解決:

  • 多個(gè)文件重復(fù)引用相同helpers(幫助函數(shù))-> 提取運(yùn)行時(shí)
  • 新API方法全局污染 -> 局部引入

這個(gè)插件推薦在編寫(xiě)library/module時(shí)使用。當(dāng)然,以上問(wèn)題可通過(guò)設(shè)置useBuiltIns搞定。

支持實(shí)例方法按需引入

傳統(tǒng)方式是手動(dòng)從core-js引入需要的ES6+特性,

require('core-js/fn/set');
require('core-js/fn/array/from');
require('core-js/fn/array/find-index');
...
...

或者一股腦全部引入:

import '@babel/polyfill'
// or
require('@babel/polyfill')

同樣,以上問(wèn)題可通過(guò)設(shè)置useBuiltIns搞定。

使用說(shuō)明

默認(rèn)情況下,@babel/preset-env的效果和@babel/preset-latest一樣,雖然上面的說(shuō)明有提到polyfills,但是也需要在配置中設(shè)置useBuiltIns才會(huì)生效。

主要配置

targets

設(shè)置支持環(huán)境,支持的key包括:chrome, opera, edge, firefox, safari, ie, ios, android, node, electron。

例如:

{
  "presets": [
    ["@babel/env", {
      "targets": {
        "node": "current",
        "chrome": 52,
        "browsers": ["last 2 versions", "safari 7"]
      }
    }]
  ]
}

其中,browserslist可在package.json中配置,這個(gè)和設(shè)置CSS的Autoprefixer一致,配置優(yōu)先級(jí)如下:

targets.browsers > package.json/browserslist

此外,browserslist的配置滿(mǎn)足最大匹配原則,比如需要同時(shí)支持在 IE 8 和 Chrome 55 下運(yùn)行,則preset-env提供所有 IE 8 下需要的插件,即使 Chrome 55 可能不需要。

modules

選項(xiàng)用于模塊轉(zhuǎn)化規(guī)則設(shè)置,可選配置包括:"amd" | "umd" | "systemjs" | "commonjs" | false, 默認(rèn)使用 "commonjs"。即,將代碼中的ES6的import轉(zhuǎn)為require

如果你當(dāng)前的webpack構(gòu)建環(huán)境是2.x/3.x,推薦將modules設(shè)置為false,即交由 Webpack 來(lái)處理模塊化,通過(guò)其 TreeShaking 特性將有效減少打包出來(lái)的 JS 文件大小。這部分參考這里的回答:ECMAScript 6 的模塊相比 CommonJS 的require (...)有什么優(yōu)點(diǎn)?

useBuiltIns

A way to apply @babel/preset-env for polyfills (via @babel/polyfill).

可選值包括:"usage" | "entry" | false, 默認(rèn)為 false,表示不對(duì) polyfills 處理,這個(gè)配置是引入 polyfills 的關(guān)鍵

"useBuiltIns":"usage"

在文件需要的位置單獨(dú)按需引入,可以保證在每個(gè)bundler中只引入一份。例如:

In

a.js

var a = new Promise();

b.js

var b = new Map();

Out (if environment doesn't support it)

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

Out (if environment supports it)

var a = new Promise();
var b = new Map();

??!注意?。?/strong>

當(dāng)前模式類(lèi)似于@babel/plugin-transform-runtime,polyfill局部使用,制造一個(gè)沙盒環(huán)境,不造成全局污染,但是如上配置后,@babel/preset-env能按需引入新實(shí)例方法,例如:

"foobar".includes("foo")

@babel/plugin-transform-runtime不行,需要自行從core-js中按需引入。我測(cè)試的情況和下面這篇文章所說(shuō)完全不一致。

原文:https://zhuanlan.zhihu.com/p/29506685

當(dāng) useBuiltIns 設(shè)置為 usage 時(shí),Babel 會(huì)在你使用到 ES2015+ 新特性時(shí),自動(dòng)添加 babel-polyfill 的引用,并且是 partial 級(jí)別的引用。

請(qǐng)注意: usage 的行為類(lèi)似 babel-transform-runtime,不會(huì)造成全局污染,因此也會(huì)不會(huì)對(duì)類(lèi)似 Array.prototype.includes() 進(jìn)行 polyfill。

"useBuiltIns":"entry"

在項(xiàng)目入口引入一次(多次引入會(huì)報(bào)錯(cuò))

import "@babel/polyfill"
// or
require("@babel/polyfill")

插件@babel/preset-env會(huì)將把@babel/polyfill根據(jù)實(shí)際需求打散,只留下必須的,例如:

In

import "@babel/polyfill";

Out (different based on environment)

import "core-js/modules/es6.promise";
import "core-js/modules/es7.string.pad-start";
import "core-js/modules/es7.string.pad-end";
import "core-js/modules/es7.array.includes";

除了新API,也可以是實(shí)例方法,不過(guò)最終包體積比使用"useBuiltIns":"usage"大30kb左右(因項(xiàng)目而異)。

"useBuiltIns": false

不在代碼中使用polyfills,表現(xiàn)形式和@babel/preset-latest一樣,當(dāng)使用ES6+語(yǔ)法及API時(shí),在不支持的環(huán)境下會(huì)報(bào)錯(cuò)。

@babel/preset-stage-x

這里需要說(shuō)明下ES特性支持的提案,不同階段的提案支持的內(nèi)容不同,其中stage-4階段提案中的特性將會(huì)在未來(lái)發(fā)布。

關(guān)于各個(gè)Stage的說(shuō)明參考這里。上面介紹的@babel/preset-env或者@babel/preset-latest就是下面提到的stage-4

The TC39 categorizes proposals into the following stages:

  • Stage 0 - Strawman: just an idea, possible Babel plugin.
  • Stage 1 - Proposal: this is worth working on.
  • Stage 2 - Draft: initial spec.
  • Stage 3 - Candidate: complete spec and initial browser implementations.
  • Stage 4 - Finished: will be added to the next yearly release.

Stage的包含順序是:左邊包含右邊全部特性,即stage-0包含右邊 1 / 2 / 3 的所有插件。

stage-0 > ~1 > ~2 > ~3 > ~4: 

疑問(wèn)

1. 這個(gè)和@babel/preset-env的區(qū)別

@babel/preset-env會(huì)根據(jù)預(yù)設(shè)的瀏覽器兼容列表從stage-4選取必須的plugin,也就是說(shuō),不引入別的stage-x,@babel/preset-env將只支持到stage-4。

建議

1. 如果是React用戶(hù),建議配到@babel/preset-stage-0

其中的兩個(gè)插件對(duì)于寫(xiě)JSX很有幫助。

  • transform-do-expressions:if/else三目運(yùn)算展開(kāi)
  • transform-function-bind:this綁定

2. 通常使用建議配到@babel/preset-stage-2

插件包括:

  • syntax-dynamic-import: 動(dòng)態(tài)import
  • transform-class-properties:用于 class 的屬性轉(zhuǎn)化
  • transform-object-rest-spread:用來(lái)處理 rest spread
  • transform-async-generator-functions:用來(lái)處理 async 和 await

@babel/polyfill

這個(gè)插件是對(duì)core-jsregenerator-runtime的再次封裝,在@babel/preset-env中的useBuiltIns: entry用到,代碼不多。

if (global._babelPolyfill) {
  throw new Error("only one instance of @babel/polyfill is allowed");
}
global._babelPolyfill = true;

import "core-js/shim";
import "regenerator-runtime/runtime";

core-js/shim

shim only: Only includes the standard methods.

只包含了納入標(biāo)準(zhǔn)的API實(shí)例化方法,例如下列常見(jiàn)的。注意,這里沒(méi)有g(shù)enerator/async,如果需要,那就安裝插件@babel/preset-stage-3(或者0 / 1 / 2)

...
require('./modules/es6.string.trim');
require('./modules/es6.string.includes');
require('./modules/es7.array.includes');
require('./modules/es6.promise');
...

regenerator-runtime/runtime

Standalone runtime for Regenerator-compiled generator and async functions.

主要是給generator/async做支持的插件。

總結(jié)

這里需要理解下三個(gè)概念:

  • 最新ES 語(yǔ)法:比如,箭頭函數(shù)
  • 最新ES API:,比如,Promise
  • 最新ES 實(shí)例方法:比如,String.protorype.includes

@babel/preset-env默認(rèn)支持語(yǔ)法轉(zhuǎn)化,需要開(kāi)啟useBuiltIns配置才能轉(zhuǎn)化API實(shí)例方法。

此外,不管是寫(xiě)項(xiàng)目還是寫(xiě)Library/Module,使用@babel/preset-env并正確配置就行。多看英文原稿說(shuō)明,中文總結(jié)看看就好,別太當(dāng)真。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容