困擾

作為工具開發者,babel 關聯問題是難繞過去的砍。但是在聊到具體的配置的時候,又有很大一部分人很難去整明白,尤其是babel的版本從 6 升級到7,導致老專案用了6,新專案用到7,很難理清具體的配置內容

舉個 ,下面就是babel常見的包:

@babel/preset-env

@babel/plugin-transform-runtime

@babel/runtime

core-js

@babel/polyfills

babel-polyfills

這麼一大堆babel相關的包,是不是熟悉又陌生,實際專案中要用哪些?該怎麼用?

這裡給大家分享一下我使用 babel@7 的心得,如果不對,歡迎大家及時指出。

@babel/preset-env

在 babel@7 推出之際,babel 官方吧 bebel preset stage 以及 es2015 es2016 等等都廢棄了,取而代之的就是 @babel/preset-env

@babel/preset-env is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s)。 This both makes your life easier and JavaScript bundles smaller!

透過官方文件的描述,@babel/preset-env 主要做的事轉換 JavaScript 最新的 syntax(如:const let => 。。。 等),而作為可選項,@babel/preset-env 也是可以轉換 JavaScript 最新的 API (如:陣列的 filter includes,Promise 等)

這裡我刻意的強調了 syntax 和 API。

小 tips:

在 babel@6 年代,我們使用的是 stage,那時的 stage 其實只會翻譯 syntax,而 API 則交給了 babel-runtime 或者 @babel/polyfill 來實現。這也是為什麼大家在老專案中可以看到有引入@babel/polyfill 的原因。

在 babel@7 年代,我們廢棄了 stage,使用的是 preset-env,同時它也可以提供 polyfill 能力。

綜上所述,我們會有幾個困惑

@babel/preset-env 已經有 polyfill 能力了,為啥還要有 @babel/plugin-transform-runtime ?這貨是必須的嗎?

@babel/preset-env 已經有 polyfill 能力了,我還需要 @babel/polyfill 嗎?

要解釋這幾個問題,我們需要了解一下 @babel/preset-env 的三個關鍵引數

target

Describes the environments you support/target for your project。

簡單講,該引數決定了我們專案需要適配到的環境,比如可以申明適配到的瀏覽器版本,這樣 babel 會根據瀏覽器的支援情況自動引入所需要的 polyfill。

useBuiltlns

“usage” | “entry” | false, defaults to false

This option configures how @babel/preset-env handles polyfills。

這個引數決定了 @babel/preset-env 如何處理 polyfill。

{ useBuiltlns: false }

這種方式下,不會引入 polyfill,你需要人為的在專案入口檔案處 import ‘@babel/polyfill’;

但是如上這種方式在 babel@7 之後被廢棄了,取而代之的是在入口檔案自行 import 如下程式碼:

import

‘core-js/stable’

import

‘regenerator-runtime/runtime’

// your code

注:不推薦採用 false,這樣會把所有的 polyfill 全部打入,造成包體積龐大。

{ useBuiltlns: ‘usage’ }

我們在專案的入口檔案處

不需要

手動 import 對應的 polyfill 相關庫,babel 會根據使用者程式碼的使用情況,並根據 targets 自行注入相關的 polyfill。

{ useBuiltlns: ‘entry’ }

我們需要在專案入口處引入對應的 polyfill 相關庫

import

‘core-js/stable’

import

‘regenerator-runtime/runtime’

// your code

此時 babel 會根據當前 targets 描述,把需要的所有的 polyfills 全部引入到你的入口檔案(注意是全部,不管你是否有用到高階的 API)

corejs

String or { version: string, proposals: boolean }, defaults to “2。0”。

注意 corejs 並不是特殊概念,而是瀏覽器的 polyfill 都由它來管了。

舉個

// 原始碼

const

one

=

Symbol

‘one’

);

// babel 後

“use strict”

require

“core-js/modules/es。symbol。description。js”

);

var

one

=

Symbol

‘one’

);

這裡或許有人可能不太清楚,2 和 3 有啥區別,可以看看官方的文件。

簡單來說就是 corejs-2 不再維護了,所有瀏覽器新 feature 的 polyfill 都會維護在 corejs-3 上。

使用 corejs-3,開啟 proposals: true,那個我們就可以使用 proposals 階段的 API 了。

總結:

使用 @babel/preset-env 注入的 polyfill 使會汙染全域性的,但是如果是自己的應用,這個結果是可控的。

所以這裡推薦的業務專案配置的 。babelrc

{

“presets”

“@babel/preset-env”

{

“targets”

{

“chrome”

“58”

//

按自己需要填寫

},

“useBuiltIns”

“entry”

“corejs”

{

“version”

3

“proposals”

true

}

}

],

“plugins”

[]

}

入口檔案

import

‘core-js/stable’

import

‘regenerator-runtime/runtime’

// your code

這樣配置的原因是:target 下設定我們業務專案所需要支援的最低環境配置,useBuiltIns 設定為 entry,將最低環境不支援的所有 polyfill 都引入到入口檔案(即使你在你的業務程式碼中並未使用)。這是一種兼顧最終打包體積和穩妥的方式,為什麼說穩妥呢,因為我們很難保證引用的三方包有處理好 polyfill 這些問題。

當然如果你能充分保證你的三方依賴 polyfill 處理得當,那麼也可以把 useBuiltIns 設定為 usage。

@babel/plugin-transform-runtime

針對大眾普通專案,可能如上方式的配置(撇開個性化)應該夠用了,

但追求極致的話會,還是會有兩個問題:

1. 會有一定的程式碼重複問題

舉個

// 原始碼

import

a

from

‘a’

export

default

a

// babel 後

“use strict”

exports

__esModule

=

true

exports

“default”

=

void

0

var

_a

=

_interopRequireDefault

require

“a”

));

// _interopRequireDefault 這個方法,明顯是可以變成一個獨立模組,這樣打包體積會變小(蚊子再小,也是肉不是!!!)

function

_interopRequireDefault

obj

{

return

obj

&&

obj

__esModule

obj

{

“default”

obj

};

}

var

_default

=

_a

“default”

];

exports

“default”

=

_default

2. 上面我們說到使用 @babel/preset-env 注入的 polyfill 使會汙染全域性的,這個在專案中是可以接受的,但是如果作為 Library 我們就不希望它會汙染全域性環境了

這個時候就是 @babel/plugin-transform-runtime 出場的機會了。

官方描述:

A plugin that enables the re-use of Babel‘s injected helper code to save on codesize。

很明顯該外掛的出現就是複用 babel 注入的關聯程式碼。

具體做了哪些事情,官方也有明確的解釋:

The transform-runtime transformer plugin does three things:

Automatically requires @babel/runtime/regenerator when you use generators/async functions (toggleable with the regenerator option)。

Can use core-js for helpers if necessary instead of assuming it will be polyfilled by the user (toggleable with the corejs option)

Automatically removes the inline Babel helpers and uses the module @babel/runtime/helpers instead (toggleable with the helpers option)。

接著上面的

import

a

from

’a‘

export

default

a

// babel 編譯後

“use strict”

var

_interopRequireDefault

=

require

“@babel/runtime/helpers/interopRequireDefault”

);

exports

__esModule

=

true

exports

“default”

=

void

0

var

_a

=

_interopRequireDefault

require

“a”

));

var

_default

=

_a

“default”

];

exports

“default”

=

_default

這就解決了上面提到的問題一。

至於問題二,關於 polyfill 全域性汙染,不打算展開,因為涉及原始碼,大家只需要知道

透過 @babel/plugin-transform-runtime 外掛實現的 polyfill 是不會影響全域性的,所以更適合 Library 作者使用

另外,可能會有好奇寶寶想到 @babel/plugin-transform-runtime 開啟 corejs 並且 @babel/preset-env 也開啟 useBuiltns 會咋樣?

結論是:被使用到的高階 API polyfill 將會採用 runtime 的不汙染全域性方案(注意:@babel/preset-env targets 設定將會失效),而不被使用到的將會採用汙染全域性的。

所以總結如下:

如果是業務專案開發者

:@babel/plugin-transform-runtime ,建議關閉 corejs,polyfill 的引入由 @babel/preset-env 完成,即開啟 useBuiltIns(如需其他配置,自行根據訴求配置)。

{

“presets”

“@babel/preset-env”

{

“targets”

{

“chrome”

58

},

“useBuiltIns”

“entry”

“corejs”

{

“version”

3

“proposals”

true

}

}

],

“plugins”

“@babel/plugin-transform-runtime”

{

“corejs”

false

}

}

並在入口檔案處 import 如下內容

import

’core-js/stable‘

import

’regenerator-runtime/runtime‘

// your code

如果是 Library 開發者

:@babel/plugin-transform-runtime ,建議開啟 corejs,polyfill 由 @babel/plugin-transform-runtime 引入。 @babel/preset-env 關閉 useBuiltIns。

{

“presets”

“@babel/preset-env”

],

“plugins”

“@babel/plugin-transform-runtime”

{

“corejs”

{

“version”

3

“proposals”

true

}

}

}

core-js

不需要深入研究它,請結合 @babel/preset-env 來看。

@babel/polyfills

已經在 babel@7。4 廢棄,請結合 @babel/preset-env 來看。

babel-polyfills

Motivation

It wasn’t possible to use @babel/preset-env‘s targets option with “pure” ponyfills, because @babel/plugin-transform-runtime is a completely separate package。

We forced our users to use core-js if they wanted a Babel integration。 core-js is a good and comprehensive polyfill, but it doesn’t fit the needs of all of our users。

但是目前這個庫處於 experimental 即試驗性的階段,並不推薦大家當前在生產中引入,我們可以開放的心態保持關注即可。

ps: 自己嚐鮮自己負責(我是求生欲極強的作者 o。o)