平時在開發的過程當中,咱們可能並不太須要十分了解babel的內容,僅僅知道它可以將新特性的代碼轉換成可以在舊版本瀏覽器中運行的代碼。可是這一次想要趁着本身搭建腳手架的機會去進一步的瞭解babel的知識,因此寫了這篇文章。如下內容是babel 7.4以後的版本,也就是@babel/polyfill
被廢棄須要獨立安裝core-js
和 regenerator-runtime
模塊的版本。npm
@babel/cli
是babel的命令行工具,主要提供babel
命令。另外還須要安裝@babel/core
才能使用babel去編譯。json
npm install --save-dev @babel/core @babel/cli
將命令配置在 package.json 文件的 scripts 字段中:promise
// package.json "scripts": { "compiler": "babel src --out-dir lib --watch" }
這樣就可以經過npm run compiler
來執行編譯,可是babel自己什麼都不作,須要添加插件來幫助babel完成工做。瀏覽器
babel全部功能都創建在各類的plugin上,使用方式是安裝相應的plugin
再去配置文件中去使用。例如箭頭函數轉換插件,
安裝@babel/plugin-transform-arrow-functions
,而後在.babelrc
配置文件中去指定對應的插件babel
//.babelrc { plugins: ["@babel/plugin-transform-arrow-functions"], };
而後執行npm run compiler
,能夠看到箭頭函數已經被編譯完成async
可是若是咱們每一個功能都去一個個添加對應的plugin
會很麻煩,多以咱們就須要preset
預設去直接添加一組插件。函數
preset
就是一組插件的集合,最經常使用的preset
就是@babel/preset-env
。工具
它的做用是根據目標環境去進行語法轉換和導入對應的polyfill
。post
須要注意的是,@babel/preset-env
會根據你配置的目標環境,生成插件列表來編譯。默認狀況下,若是你沒有在 Babel 配置文件中(如 .babelrc)設置 targets 或 ignoreBrowserslistConfig,@babel/preset-env
會使用 package.json
的browserslist
配置源。ui
咱們能夠模擬生產環境和開發環境的瀏覽器版本
const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"];
經過設置不一樣瀏覽器環境使用@babel/preset-env
去編譯相同代碼,能夠看到最終的結果也會不一樣。
module.exports = { presets: [ [ "@babel/preset-env", { // targets: product, targets: development, }, ], ], };
babel 只負責對語法進行編譯,好比當咱們寫箭頭函數,babel 會幫你把它編譯成普通函數。可是對一些新的擴展方法,新的類來講babel就不能轉換了。這時就須要去引入polyfill
,polyfill
的中文意思是墊片,所謂墊片就是墊平不一樣瀏覽器或者不一樣環境下的差別,讓新的內置函數、實例方法等在低版本瀏覽器中也可使用。
babel v7.4版以後,須要直接安裝core-js
和 regenerator-runtime
去替代以前的@babel/polyfill
。croe-js
提供了 ES五、ES6 規範中新定義的各類對象、方法的polyfill,regenerator-runtime
用來實現 ES6/ES7 中 generators、yield、async 及 await 等相關的 polyfill。
首先,咱們須要安裝他們到生產環境中,由於須要在生產環境中運行其中的polyfill
。
npm install --save core-js regenerator-runtime
在@babel/preset-env
的配置項中把useBuiltIns
設置成usage
,這樣會根據目標瀏覽器環境去引入所須要的polyfill
。須要注意點是,設置useBuiltIns
還須要同時設置corejs
。
//.babelrc const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"]; module.exports = { presets: [ [ "@babel/preset-env", { targets: development, useBuiltIns: "usage", corejs: 3, }, ], ], };
//index.js const isHas = [1, 2, 3].includes(2); const getData = () => new Promise((resolve, reject) => { setTimeout(() => { resolve(100); }, 1000); }); const main = async () => { const res = await getData(); console.log(res); }; main();
編譯後的文件:
能夠看到,編譯後的文件中只引入了所用到的polyfill。
useBuiltIns
還能夠設置成其餘值,好比entry
,這須要在項目入口文件手動引入polyfills,例如@babel/polyfill
或者core-js
//.babelrc module.exports = { presets: [ [ "@babel/preset-env", { targets: product, useBuiltIns: "entry", corejs: 3, }, ], ], }; //index.js // 入口文件引入core-js require("core-js");
可是這種方式會引入全量的polyfill。
useBuiltIns
默認值爲false
,表明每一個文件裏不自動添加polyfill,或不將import "@babel/polyfill"
轉換爲單獨的polyfill。
在使用@babel/preset-env
配合useBuiltIns: usage
時,文件中會引入一些輔助方法例如_classCallCheck
,當多處文件都使用到class時一樣也會在每一個文件中去引入這些輔助方法,這樣會增大打包體積而且徹底沒有必要屢次去引入一樣的輔助方法。
//index.js class A {} //.babelrc.js const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"]; module.exports = { presets: [ [ "@babel/preset-env", { targets: product, useBuiltIns: "usage", corejs: 3, }, ], ], };
編譯結果:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var A = function A() { _classCallCheck(this, A); };
爲了解決這個問題就須要使用@babel/plugin-transform-runtime
,使用該插件,全部輔助方法都將引用模塊 @babel/runtime
,這樣就能夠避免編譯後的代碼中出現重複的輔助方法,有效減小包體積。
@babel/plugin-transform-runtime
須要配合@babel/runtime
來使用,@babel/plugin-transform-runtime
在開發時使用,最終代碼須要依賴@babel/runtime
。
npm install --save-dev @babel/plugin-transform-runtime npm install --save @babel/runtime
//index.js class A {} //.babelrc.js const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"]; module.exports = { presets: [ [ "@babel/preset-env", { targets: product, useBuiltIns: "usage", corejs: 3, }, ], ], //使用@babel/plugin-transform-runtime plugins: [["@babel/plugin-transform-runtime"]], };
編譯結果:
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var A = function A() { (0, _classCallCheck2.default)(this, A); };
能夠看到這些輔助方法都是從@babel/runtime
中引入。
以前在使用@babel/preset-env
編譯promise和includes時會引入core-js
中的全局變量或者在對應的原型鏈中添加相應的方法,這樣都形成了全局環境的污染。雖然這對於應用程序或命令行工具是能夠的,可是若是你的代碼是要發佈供他人使用的庫,或者沒法徹底控制代碼運行的環境,則將成爲一個問題。
首先,單獨使用@babel/plugin-transform-runtime
只可以處理輔助方法,若是想要去引入polyfill
就須要配合@babel/runtime-corejs3
使用。
一樣仍是在生產環境安裝@babel/runtime-corejs3
。
npm install @babel/runtime-corejs3 --save
這裏須要在.babelrc
中去除@babel/preset-env
配置中關於polyfill
的部分以避免與@babel/runtime-corejs3
重複。
//index.js const isHas = [1, 2, 3].includes(2); const getData = () => new Promise((resolve, reject) => { setTimeout(() => { resolve(100); }, 1000); }); getData(); //.babelrc.js module.exports = { presets: [["@babel/preset-env"]], plugins: [ [ "@babel/plugin-transform-runtime", { corejs: 3, }, ], ], };
編譯結果:
"use strict"; var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault"); var _setTimeout2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-timeout")); var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise")); var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes")); var _context; var isHas = (0, _includes["default"])(_context = [1, 2, 3]).call(_context, 2); var getData = function getData() { return new _promise["default"](function (resolve, reject) { (0, _setTimeout2["default"])(function () { resolve(100); }, 1000); }); }; getData();
能夠看到,使用@babel/plugin-transform-runtime
會用一個臨時變量去保存polyfill中的一些值,並非直接去修改原型鏈或者新增Promise方法。
@babel/preset-env
配合useBuiltIns: usage
,在開發第三方庫時使用@babel/plugin-transform-runtime
在上面介紹@babel/plugin-transform-runtime
的一些使用時能夠看到,它不只可以處理引入屢次helper輔助方法的問題,並且在只引入所需polyfill時還不會污染全局環境,那還有必要使用@babel/preset-env
的useBuiltIns
嗎?
其實@babel/plugin-transform-runtime
配合@babel/runtime-corejs3
引入polyfill有一個很大的不足就是不可以經過設置目標環境去引入所須要的polyfil。,咱們在普通開發時只須要在package.json
中的browserslist
去設置開發環境和生產環境的瀏覽器版本,而後經過使用@babel/preset-env
的useBuiltIns
就可以根據不一樣的運行環境去引入適當的polyfill。
可是在開發第三方庫時,不能肯定代碼的運行環境,因此就須要利用@babel/plugin-transform-runtime
來保證引入的polyfill不去污染全局環境。
通常開發: 經過useBuiltIns: usage
去保證引入恰當的polyfill,經過@babel/plugin-transform-runtime
保證輔助函數都是引用@babel/runtime
`。
const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"]; module.exports = { presets: [ [ "@babel/preset-env", { //代替browserslist設置瀏覽器版本 targets: product, useBuiltIns: "usage", corejs: 3, }, ], ], plugins: [["@babel/plugin-transform-runtime"]], };