本文首發(fā)于 vivo互聯(lián)網(wǎng)技術(shù) 微信公眾號(hào)
鏈接: https://mp.weixin.qq.com/s/plJewhUd0xDXh3Ce4CGpHg
作者:Morrain
一翘县、前言
在上一節(jié) 《CommonJS:不是前端卻革命了前端》中叮称,我們聊到了 ES6 Module萧诫,它是 ES6 中對(duì)模塊的規(guī)范,ES6 是 ECMAScript 6.0 的簡(jiǎn)稱葡幸,泛指 JavaScript 語(yǔ)言的下一代標(biāo)準(zhǔn)软驰,它的第一個(gè)版本 ES2015 已經(jīng)在 2015 年 6 月正式發(fā)布载城,本文中提到的 ES6 包括 ES2015瞧捌、ES2016、ES2017等等义黎。在第一節(jié)的《Web:一路前行一路忘川》中也提到過禾进,ES2015 從制定到發(fā)布?xì)v經(jīng)了十幾年,引入了很多的新特性以及新的機(jī)制轩缤,瀏覽器對(duì) ES6 的支持進(jìn)度遠(yuǎn)遠(yuǎn)趕不上前端開發(fā)小哥哥們使用 ES6 的熱情命迈,于是矛盾就日益顯著……
二贩绕、Babel 是什么
先來(lái)看下它在官網(wǎng)上的定義:
Babel is a JavaScript compiler
沒錯(cuò)就一句話,Babel 是 JavaScript 的編譯器壶愤。至于什么是編譯器淑倾,可以參考the-super-tiny-compiler這個(gè)項(xiàng)目,可以找到很好的答案征椒。
本文是以 Babel 7.9.0 版本進(jìn)行演示和講解的娇哆,另外建議學(xué)習(xí)者閱讀英文官網(wǎng),中文官網(wǎng)會(huì)比原版網(wǎng)站慢一個(gè)版本勃救,并且很多依然是英文的碍讨。
Babel 就是一套解決方案,用來(lái)把 ES6 的代碼轉(zhuǎn)化為瀏覽器或者其它環(huán)境支持的代碼蒙秒。注意我的用詞哈勃黍,我說(shuō)的不是轉(zhuǎn)化為 ES5 ,因?yàn)椴煌愋鸵约安煌姹镜臑g覽器對(duì) ES6 新特性的支持程度都不一樣晕讲,對(duì)于瀏覽器已經(jīng)支持的部分覆获,Babel 可以不轉(zhuǎn)化,所以 Babel 會(huì)依賴瀏覽器的版本瓢省,后面會(huì)講到弄息。這里可以先參考browerslist項(xiàng)目。
Babel 的歷史
在學(xué)習(xí)任何一門知識(shí)前勤婚,我都習(xí)慣先了解它的歷史摹量,這樣才能深刻理解它存在意義。
Babel 的作者是 FaceBook 的工程師 Sebastian McKenzie馒胆。他在 2014 年發(fā)布了一款 JavaScript 的編譯器 6to5缨称。從名字就能看出來(lái),它主要的作用就是將 ES6 轉(zhuǎn)化為 ES5祝迂。
這里的 ES6 指 ES2015具钥,因?yàn)楫?dāng)時(shí)還沒有正式發(fā)布, ES2015 的名字還未被正式確定液兽。
于是很多人評(píng)價(jià),6to5 只是 ES6 得到支持前的一個(gè)過渡方案掌动,它的作者非常不同意這個(gè)觀點(diǎn)四啰,認(rèn)為 6to5 不光會(huì)按照標(biāo)準(zhǔn)逐步完善,依然具備非常大的潛力反過來(lái)影響并推進(jìn)標(biāo)準(zhǔn)的制定粗恢。正因?yàn)槿绱?6to5 的團(tuán)隊(duì)覺得 '6to5' 這個(gè)名字并沒有準(zhǔn)確的傳達(dá)這個(gè)項(xiàng)目的目標(biāo)柑晒。加上 ES6 正式發(fā)布后,被命名為 ES2015眷射,對(duì)于 6to5 來(lái)說(shuō)更偏離了它的初衷匙赞。于是 2015 年 2 月 15 號(hào)佛掖,6to5 正式更名為 Babel。
(圖片來(lái)源于網(wǎng)絡(luò))
Babel 是巴比倫文化里的通天塔涌庭,用來(lái)給 6to5 這個(gè)項(xiàng)目命名真得太貼切了芥被!羨慕這些牛逼的人,不光代碼寫得好坐榆,還這么有文化拴魄,不像我們,起個(gè)變量名都得憋上半天席镀,吃了沒有文化的虧匹中。這也是為什么我把這篇文章起名為 《Babel:把 ES6 送上天的通天塔》的原因。
三豪诲、Babel 怎么用
了解了 Babel 是什么后顶捷,很明顯我們就要開始考慮怎么使用 Babel 來(lái)轉(zhuǎn)化 ES6 的代碼了,除了 Babel 本身提供的 cli 等工具外屎篱,它還支持和其它打包工具配合使用服赎,譬如 webpack、rollup 等等芳室,可以參考官網(wǎng)對(duì)不同平臺(tái)提供的配置說(shuō)明专肪。
本文為了感受 Babel 最原始的用法,不結(jié)合其它任何工具堪侯,直接使用 Babel 的 cli 來(lái)演示嚎尤。
1、構(gòu)建 Babel 演示的工程
使用如下命令構(gòu)建一個(gè) npm 包伍宦,并新建 src 目錄 和 一個(gè) index.js 文件芽死。
npm init -y
package.json 內(nèi)容如下:
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
2、安裝依賴包
npm install --save-dev @babel/core @babel/cli @babel/preset-env
后面會(huì)介紹這些包的作用次洼,先看用法
增加 babel 命令來(lái)編譯 src 目錄下的文件到 dist 目錄:
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"scripts": {
"babel": "babel src --out-dir dist",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.0"
}
}
3关贵、增加 Babel 配置文件
在工程的根目錄添加 babel.config.js 文件,增加 Babel 編譯的配置卖毁,沒有配置是不進(jìn)行編譯的揖曾。
const presets = [
[
'@babel/env',
{
debug: true
}
]
]
const plugins = []
module.exports = { presets, plugins }
上例中 debug 配置是為了打印出 Babel 工作時(shí)的日志,可以方便的看來(lái)亥啦,Babel 轉(zhuǎn)化了哪些語(yǔ)法炭剪。
- presets 主要是配置用來(lái)編譯的預(yù)置,plugins 主要是配置完成編譯的插件翔脱,具體的含義后面會(huì)講
- 推薦用 Javascript 文件來(lái)寫配置文件奴拦,而不是 JSON 文件,這樣可以根據(jù)環(huán)境來(lái)動(dòng)態(tài)配置需要使用的 presets 和 plugins
const presets = [
[
'@babel/env',
{
debug: true
}
]
]
const plugins = []
if (process.env["ENV"] === "prod") {
plugins.push(...)
}
module.exports = { presets, plugins }
4届吁、編譯的結(jié)果
配置好后错妖,我們運(yùn)行 npm run babel 命令绿鸣,可以看到 dist 文件夾下生成了 index.js 文件,內(nèi)容如下所示:
// src/index.js
const add = (a, b) => a + b
// dist/index.js
"use strict";
var add = function add(a, b) {
return a + b;
};
可以看到暂氯,ES6 的 const 被轉(zhuǎn)化為 var 潮模,箭頭函數(shù)被轉(zhuǎn)化為普通函數(shù)。同時(shí)打印出來(lái)如下日志:
> babel src --out-dir dist
@babel/preset-env: `DEBUG` option
Using targets:
{}
Using modules transform: auto
Using plugins:
proposal-nullish-coalescing-operator {}
proposal-optional-chaining {}
proposal-json-strings {}
proposal-optional-catch-binding {}
transform-parameters {}
proposal-async-generator-functions {}
proposal-object-rest-spread {}
transform-dotall-regex {}
proposal-unicode-property-regex {}
transform-named-capturing-groups-regex {}
transform-async-to-generator {}
transform-exponentiation-operator {}
transform-template-literals {}
transform-literals {}
transform-function-name {}
transform-arrow-functions {}
transform-block-scoped-functions {}
transform-classes {}
transform-object-super {}
transform-shorthand-properties {}
transform-duplicate-keys {}
transform-computed-properties {}
transform-for-of {}
transform-sticky-regex {}
transform-unicode-regex {}
transform-spread {}
transform-destructuring {}
transform-block-scoping {}
transform-typeof-symbol {}
transform-new-target {}
transform-regenerator {}
transform-member-expression-literals {}
transform-property-literals {}
transform-reserved-words {}
transform-modules-commonjs {}
proposal-dynamic-import {}
Using polyfills: No polyfills were added, since the `useBuiltIns` option was not set.
Successfully compiled 1 file with Babel.
四株旷、Babel 工作原理
在了解了如何使用后沼侣,我們一起來(lái)探尋一下編譯背后的事情颤诀,同時(shí)會(huì)熟悉 Babel 的組成和進(jìn)階用法象踊。
1侍郭、Babel 工作流程
前面提到 Babel 其實(shí)就是一個(gè)純粹的 JavaScript 的編譯器,任何一個(gè)編譯器工作流程大致都可以分為如下三步:
Parser 解析源文件
Transfrom 轉(zhuǎn)換
Generator 生成新文件
Babel 也不例外齿尽,如下圖所示:
(圖片來(lái)源于網(wǎng)絡(luò))
因?yàn)?Babel 使用是acorn這個(gè)引擎來(lái)做解析沽损,這個(gè)庫(kù)會(huì)先將源碼轉(zhuǎn)化為抽象語(yǔ)法樹 (AST),再對(duì) AST 作轉(zhuǎn)換循头,最后將轉(zhuǎn)化后的 AST 輸出绵估,便得到了被 Babel 編譯后的文件。
那 Babel 是如何知道該怎么轉(zhuǎn)化的呢卡骂?答案是通過插件国裳,Babel 為每一個(gè)新的語(yǔ)法提供了一個(gè)插件,在 Babel 的配置中配置了哪些插件全跨,就會(huì)把插件對(duì)應(yīng)的語(yǔ)法給轉(zhuǎn)化掉缝左。插件被命名為 @babel/plugin-xxx 的格式。
2浓若、Babel 組成
(1)@babel/preset-env
上面提到過 @babel/preset-* 其實(shí)是轉(zhuǎn)換插件的集合渺杉,最常用的就是 @babel/preset-env,它包含了 大部分 ES6 的語(yǔ)法挪钓,具體包括哪些插件是越,可以在 Babel 的日志中看到。如果源碼中使用了不在 @babel/preset-env 中的語(yǔ)法碌上,會(huì)報(bào)錯(cuò)倚评,手動(dòng)在 plugins 中增加即可。
例如 ES6 明確規(guī)定馏予,Class 內(nèi)部只有靜態(tài)方法蔓纠,沒有靜態(tài)屬性。但現(xiàn)在有一個(gè)提案提供了類的靜態(tài)屬性吗蚌,寫法是在實(shí)例屬性的前面,加上 static 關(guān)鍵字纯出。
// src/index.js
const add = (a, b) => a + b
class Person {
static a = 'a';
static b;
name = 'morrain';
age = 18
}
編譯時(shí)就會(huì)報(bào)如下錯(cuò)誤:
根據(jù)報(bào)錯(cuò)的提示蚯妇,添加 @babel/plugin-proposal-class-properties 即可敷燎。
npm install --save-dev @babel/plugin-proposal-class-properties
// babel.config.js
const presets = [
[
'@babel/env',
{
debug: true
}
]
]
const plugins = ['@babel/plugin-proposal-class-properties']
module.exports = { presets, plugins }
@babel/preset-env 中還有一個(gè)非常重要的參數(shù) targets,最早的時(shí)候我們就提過箩言,Babel 轉(zhuǎn)譯是按需的硬贯,對(duì)于環(huán)境支持的語(yǔ)法可以不做轉(zhuǎn)換的。就是通過配置 targets 屬性陨收,讓 Babel 知道目標(biāo)環(huán)境饭豹,從而只轉(zhuǎn)譯環(huán)境不支持的語(yǔ)法。如果沒有配置會(huì)默認(rèn)轉(zhuǎn)譯所有 ES6 的語(yǔ)法务漩。
// src/index.js
const add = (a, b) => a + b
// dist/index.js 沒有配置targets
"use strict";
var add = function add(a, b) {
return a + b;
};
按如下配置** targets**
// babel.config.js
const presets = [
[
'@babel/env',
{
debug: true,
targets: {
chrome: '58'
}
}
]
]
const plugins = ['@babel/plugin-proposal-class-properties']
module.exports = { presets, plugins }
編譯后的結(jié)果如下:
// src/index.js
const add = (a, b) => a + b
// dist/index.js 配置targets chrome 58
"use strict";
const add = (a, b) => a + b;
可以看到 const 和箭頭函數(shù)都沒有被轉(zhuǎn)譯拄衰,因?yàn)檫@個(gè)版本的 chrome 已經(jīng)支持了這些特性《牵可以根據(jù)需求靈活的配置目標(biāo)環(huán)境翘悉。
為后方便后續(xù)的講解,把 targets 的配置去掉居触,讓 Babel 默認(rèn)轉(zhuǎn)譯所有語(yǔ)法妖混。
(2)@babel/polyfill
polyfill 直譯是墊片的意思,又是 Babel 里一個(gè)非常重要的概念轮洋。先看下面幾行代碼:
// src/index.js
const add = (a, b) => a + b
const arr = [1, 2]
const hasThreee = arr.includes(3)
new Promise()
按之前的方法制市,執(zhí)行 npm run babel 后,我們驚奇的發(fā)現(xiàn)弊予,Array.prototype.includes 和 Promise 竟然沒有被轉(zhuǎn)譯祥楣!
// dist/index.js
"use strict";
var add = function add(a, b) {
return a + b;
};
var arr = [1, 2];
var hasThreee = arr.includes(3);
new Promise();
原來(lái) Babel 把 ES6 的標(biāo)準(zhǔn)分為 syntax 和 built-in 兩種類型。syntax 就是語(yǔ)法块促,像 const荣堰、=> 這些默認(rèn)被 Babel 轉(zhuǎn)譯的就是 syntax 的類型。而對(duì)于那些可以通過改寫覆蓋的語(yǔ)法就認(rèn)為是 built-in竭翠,像 includes 和 Promise 這些都屬于 built-in振坚。而 Babel 默認(rèn)只轉(zhuǎn)譯 syntax 類型的,對(duì)于 built-in 類型的就需要通過 @babel/polyfill 來(lái)完成轉(zhuǎn)譯斋扰。@babel/polyfill 實(shí)現(xiàn)的原理也非常簡(jiǎn)單渡八,就是覆蓋那些 ES6 新增的 built-in。示意如下:
Object.defineProperty(Array.prototype, 'includes',function(){
...
})
由于 Babel 在 7.4.0 版本中宣布廢棄 @babel/polyfill 传货,而是通過 core-js 替代屎鳍,所以本文直接使用 core-js 來(lái)講解 polyfill 的用法。
- 安裝 core-js
npm install --save core-js
注意 core-js 要使用 --save 方式安裝问裕,因?yàn)樗切枰蛔⑷氲皆创a中的逮壁,在執(zhí)行代碼前提供執(zhí)行環(huán)境,用來(lái)實(shí)現(xiàn) built-in 的注入
-
配置 useBuiltIns
在 @babel/preset-env 中通過 useBuiltIns 參數(shù)來(lái)控制 built-in 的注入粮宛。它可以設(shè)置為 'entry'窥淆、'usage' 和 false 卖宠。默認(rèn)值為 false,不注入墊片忧饭。
設(shè)置為 'entry' 時(shí)扛伍,只需要在整個(gè)項(xiàng)目的入口處,導(dǎo)入 core-js 即可词裤。
// src/index.js
import 'core-js'
const add = (a, b) => a + b
const arr = [1, 2]
const hasThreee = arr.includes(3)
new Promise()
// dist/index.js
"use strict";
require("core-js/modules/es7.array.includes");
require("core-js/modules/es6.promise");
//
// …… 這里還有很多
//
require("regenerator-runtime/runtime");
var add = function add(a, b) {
return a + b;
};
var arr = [1, 2];
var hasThreee = arr.includes(3);
new Promise();
- 編譯后刺洒,Babel 會(huì)把目標(biāo)環(huán)境不支持的所有 built-in 都注入進(jìn)來(lái),不管是不是用到吼砂,這有一個(gè)問題逆航,對(duì)于只用到比較少的項(xiàng)目來(lái)說(shuō)完全沒有必要,白白增加代碼帅刊,浪費(fèi)包體大小纸泡。
設(shè)置為 'usage' 時(shí),就不用在項(xiàng)目的入口處赖瞒,導(dǎo)入 core-js了女揭,Babel 會(huì)在編譯源碼的過程中根據(jù) built-in 的使用情況來(lái)選擇注入相應(yīng)的實(shí)現(xiàn)。
// src/index.js
const add = (a, b) => a + b
const arr = [1, 2]
const hasThreee = arr.includes(3)
new Promise()
// dist/index.js
"use strict";
require("core-js/modules/es6.promise");
require("core-js/modules/es6.object.to-string");
require("core-js/modules/es7.array.includes");
var add = function add(a, b) {
return a + b;
};
var arr = [1, 2];
var hasThreee = arr.includes(3);
new Promise();
- 配置 corejs 的版本
當(dāng) useBuiltIns 設(shè)置為 'usage' 或者 'entry' 時(shí)栏饮,還需要設(shè)置 @babel/preset-env 的 corejs 參數(shù)吧兔,用來(lái)指定注入 built-in 的實(shí)現(xiàn)時(shí),使用 corejs 的版本袍嬉。否則 Babel 日志輸出會(huì)有一個(gè)警告境蔼。
最終的 Babel 配置如下:
// babel.config.js
const presets = [
[
'@babel/env',
{
debug: true,
useBuiltIns: 'usage',
corejs: 3,
targets: {}
}
]
]
const plugins = ['@babel/plugin-proposal-class-properties']
module.exports = { presets, plugins }
(3)@babel/plugin-transform-runtime
在介紹 @babel/plugin-transform-runtime 的用途之前,先前一個(gè)例子:
// src/index.js
const add = (a, b) => a + b
const arr = [1, 2]
const hasThreee = arr.includes(3)
new Promise(resolve=>resolve(10))
class Person {
static a = 1;
static b;
name = 'morrain';
age = 18
}
// dist/index.js
"use strict";
require("core-js/modules/es.array.includes");
require("core-js/modules/es.object.define-property");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.promise");
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var add = function add(a, b) {
return a + b;
};
var arr = [1, 2];
var hasThreee = arr.includes(3);
new Promise(function (resolve) {
return resolve(10);
});
var Person = function Person() {
_classCallCheck(this, Person);
_defineProperty(this, "name", 'morrain');
_defineProperty(this, "age", 18);
};
_defineProperty(Person, "a", 1);
_defineProperty(Person, "b", void 0);
在編譯的過程中伺通,對(duì)于 built-in 類型的語(yǔ)法通過 require("core-js/modules/xxxx") polyfill 的方式來(lái)兼容箍土,對(duì)于 syntax 類型的語(yǔ)法在轉(zhuǎn)譯的過程會(huì)在當(dāng)前模塊中注入類似 _classCallCheck 和 _defineProperty 的 helper 函數(shù)來(lái)實(shí)現(xiàn)兼容。對(duì)于一個(gè)模塊而言罐监,可能還好吴藻,但對(duì)于項(xiàng)目中肯定是很多模塊,每個(gè)模塊模塊都注入這些 helper 函數(shù)弓柱,勢(shì)必會(huì)造成代碼量變得很大沟堡。
而 @babel/plugin-transform-runtime 就是為了復(fù)用這些 helper 函數(shù),縮小代碼體積而生的矢空。當(dāng)然除此之外航罗,它還能為編譯后的代碼提供一個(gè)沙箱環(huán)境,避免全局污染屁药。
使用 @babel/plugin-transform-runtime
- ①安裝
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
其中 @babel/plugin-transform-runtime 是編譯時(shí)使用的粥血,安裝為開發(fā)依賴,而 @babel/runtime 其實(shí)就是 helper 函數(shù)的集合,需要被引入到編譯后代碼中立莉,所以安裝為生產(chǎn)依賴
- ②修改 Babel plugins 配置绢彤,增加@babel/plugin-transform-runtime
// babel.config.js
const presets = [
[
'@babel/env',
{
debug: true,
useBuiltIns: 'usage',
corejs: 3,
targets: {}
}
]
]
const plugins = [
'@babel/plugin-proposal-class-properties',
[
'@babel/plugin-transform-runtime'
]
]
module.exports = { presets, plugins }
- 之前的例子,再次編譯后蜓耻,可以看到,之前的 helper 函數(shù)械巡,都變成類似require("@babel/runtime/helpers/classCallCheck") 的實(shí)現(xiàn)了刹淌。
// dist/index.js
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
require("core-js/modules/es.array.includes");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.promise");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var add = function add(a, b) {
return a + b;
};
var arr = [1, 2];
var hasThreee = arr.includes(3);
new Promise(function (resolve) {
return resolve(10);
});
var Person = function Person() {
(0, _classCallCheck2["default"])(this, Person);
(0, _defineProperty2["default"])(this, "name", 'morrain');
(0, _defineProperty2["default"])(this, "age", 18);
};
(0, _defineProperty2["default"])(Person, "a", 1);
(0, _defineProperty2["default"])(Person, "b", void 0);
- 配置 @babel/plugin-transform-runtime
到目前為止,對(duì)于 built-in 類型的語(yǔ)法還是通過 require("core-js/modules/xxxx") polyfill 的方式來(lái)實(shí)現(xiàn)的讥耗,例如為了支持 Array.prototype.includes 方法有勾,需要 require
("core-js/modules/es.array.includes") 在 Array.prototype 中添加 includes 方法來(lái)實(shí)現(xiàn)的,但這會(huì)導(dǎo)致一個(gè)問題古程,它是直接修改原型的蔼卡,會(huì)造成全局污染。如果你開發(fā)的是獨(dú)立的應(yīng)用問題不大挣磨,但如果開發(fā)的是工具庫(kù)雇逞,被其它項(xiàng)目引用,而恰好該項(xiàng)目自身實(shí)現(xiàn)了 Array.prototype.includes 方法茁裙,這樣就出了大問題塘砸!而 @babel/plugin-transform-runtime 可以解決這個(gè)問題,只需要配置 @babel/plugin-transform-runtime 的參數(shù) corejs晤锥。該參數(shù)默認(rèn)為 false掉蔬,可以設(shè)置為 2 或者 3,分別對(duì)應(yīng) @babel/runtime-corejs2 和 @babel/runtime-corejs3矾瘾。
把 @babel/plugin-transform-runtime 的 corejs 的值設(shè)置為3女轿,把 @babel/runtime 替換為 @babel/runtime-corejs3。
去掉 @babel/preset-env 的 useBuiltIns 和 corejs 的配置壕翩,去掉 core-js蛉迹。因?yàn)槭褂?@babel/runtime-corejs3 來(lái)實(shí)現(xiàn)對(duì) built-in 類型語(yǔ)法的兼容,不用再使用 useBuiltIns了戈泼。
npm uninstall @babel/runtime
npm install --save @babel/runtime-corejs3
npm uninstall core-js
// babel.config.js
const presets = [
[
'@babel/env',
{
debug: true,
targets: {}
}
]
]
const plugins = [
'@babel/plugin-proposal-class-properties',
[
'@babel/plugin-transform-runtime',
{
corejs: 3
}
]
]
module.exports = { presets, plugins }
// dist/index.js
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/defineProperty"));
var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
var add = function add(a, b) {
return a + b;
};
var arr = [1, 2];
var hasThreee = (0, _includes["default"])(arr).call(arr, 3);
new _promise["default"](function (resolve) {
return resolve(10);
});
var Person = function Person() {
(0, _classCallCheck2["default"])(this, Person);
(0, _defineProperty2["default"])(this, "name", 'morrain');
(0, _defineProperty2["default"])(this, "age", 18);
};
(0, _defineProperty2["default"])(Person, "a", 1);
(0, _defineProperty2["default"])(Person, "b", void 0);
可以看到 Promise 和 arr.includes 的實(shí)現(xiàn)已經(jīng)變成局部變量婿禽,并沒有修改全局上的實(shí)現(xiàn)。
3大猛、Babel polyfill 實(shí)現(xiàn)方式的區(qū)別
截至目前為止扭倾,對(duì)于 built-in 類型的語(yǔ)法的 polyfill,一共有三種方式:
使用 @babel/preset-env 挽绩,useBuiltIns 設(shè)置為 'entry'
使用 @babel/preset-env 膛壹,useBuiltIns 設(shè)置為 'usage'
使用 @babel/plugin-transform-runtime
前兩種方式支持設(shè)置 targets ,可以根據(jù)目標(biāo)環(huán)境來(lái)適配。useBuiltIns 設(shè)置為 'entry' 會(huì)注入目標(biāo)環(huán)境不支持的所有 built-in 類型語(yǔ)法模聋,useBuiltIns 設(shè)置為 'usage' 會(huì)注入目標(biāo)環(huán)境不支持的所有被用到的 built-in 類型語(yǔ)法肩民。注入的 built-in 類型的語(yǔ)法會(huì)污染全局。
第三種方式目前不支持設(shè)置 targets链方,所以不會(huì)考慮目標(biāo)環(huán)境是否已經(jīng)支持持痰,它是通過局部變量的方式實(shí)現(xiàn)了所有被用到的 built-in 類型語(yǔ)法,不會(huì)污染全局祟蚀。
針對(duì)第三種方式不支持設(shè)置 targets 的問題工窍,Babel 正在考慮解決,目前意向的方案是通過** Polyfill provider** 來(lái)統(tǒng)一 polyfill 的實(shí)現(xiàn):
廢棄 @babel/preset-env 中 useBuiltIns 和 corejs 兩個(gè)參數(shù)前酿,不再通過 @babel/preset-env 實(shí)現(xiàn) polyfill患雏。
廢棄 @babel/plugin-transform-runtime 中的 corejs 參數(shù),也不再通過 @babel/plugin-transform-runtime 來(lái)實(shí)現(xiàn) polyfill罢维。
增加 polyfills 參數(shù)淹仑,類似于現(xiàn)在 presets 和 plugins,用來(lái)取代現(xiàn)在的 polyfill 方案肺孵。
把 @babel/preset-env 中 targets 參數(shù)匀借,往上提一層,和 presets悬槽、plugins怀吻、polyfills 同級(jí)別,并由它們共享初婆。
這個(gè)方案實(shí)現(xiàn)后蓬坡,Babel 的配置會(huì)是下面的樣子:
// babel.config.js
const targets = [
'>1%'
]
const presets = [
[
'@babel/env',
{
debug: true
}
]
]
const plugins = [
'@babel/plugin-proposal-class-properties'
]
const polyfills = [
[
'corejs3',
{
method: 'usage-pure'
}
]
]
module.exports = { targets, presets, plugins, polyfills }
配置中的 method 值有 'entry-global'、'usage-global'磅叛、'usage-pure' 三種屑咳。
'entry-global' 等價(jià)于 @babel/preset-env 中的 useBuiltIns: 'entry'
'usage-global' 等價(jià)于 @babel/preset-env 中的 useBuiltIns: 'usage'
'usage-pure' 等價(jià)于 @babel/plugin-transform-runtime 中的 corejs
本文為了講解方便,都是用 Babel 原生的 @babel/cli 來(lái)編譯文件弊琴,實(shí)際使用中兆龙,更多的是結(jié)合 webpack、rollup 這樣第三方的工具來(lái)使用的敲董。
所以下一節(jié)紫皇,我們聊聊打包工具 webpack。