🤖 作者簡介:水煮白菜王,一位前端勸退師 👻
👀 文章專欄: 前端專欄 ,記錄一下平時在博客寫作中,總結出的一些開發技巧和知識歸納總結?。
感謝支持💕💕💕
目錄
- @babel/preset-env
- @babel/polyfill
- babel-runtime
- 主要功能
- 補充
- babel-plugin-transform-runtime
- 配置
- 示例
- 控制引入 babel-runtime
- 控制 corejs 配置
在前端工程化領域,包體積優化一直是一個備受關注的話題。隨著項目規模擴大和功能迭代,打包后的文件體積逐漸膨脹。而對于網頁加載速度和性能優化來說,減小打包體積是至關重要的一環。
為此,在這篇文章中,將探討如何利用 babel-runtime 這一工具來幫助我們將重復的定義通過模塊導入的方式引入,縮減打包體積以提升項目性能。
Babel 是一個被廣泛使用的 JS 編譯器,用于將新語法轉換為向后兼容的 JS 代碼。一般情況下,我們可以通過安裝預設和插件控制 ****Babel 的代碼轉譯,比如:
預設(Presets 一組預定義的轉換規則的集合)
- @babel/preset-env:這是 Babel 官方推薦的預設之一,用于根據目標環境自動確定所需的轉換和 polyfill。
- @babel/preset-react:用于支持 React 項目中的 JSX 和其他相關特性的預設。·
- @babel/preset-typescript:用于支持 TS 項目中的預設,能夠將 TS 代碼轉換為 JS 代碼。
插件(Plugins 單個轉換規則的集合)
4. @babel/plugin-proposal-class-properties:用于支持 JS 類的屬性初始化器,包括靜態屬性和實例屬性。
5. @babel/plugin-transform-arrow-functions:將箭頭函數轉換為普通函數表達式,以提供更廣泛的兼容性。
6. @babel/plugin-transform-runtime:將 Babel 編譯時注入的輔助函數轉換為引用運行時公共函數的方式,以減小輸出文件的體積。
@babel/preset-env
@babel/preset-env 的主要功能包括:
-
自動 polyfill:根據目標環境自動導入所需的 polyfill,從而實現對新特性的兼容性支持。
-
智能轉換:基于目標環境的瀏覽器或 Node 版本來自動轉換 ES6+ 語法或 API。
-
模塊轉換:支持將模塊轉換為不同類型(CommonJS、AMD、UMD 等)的模塊系統。
-
按需加載:支持根據需要選擇和加載特定的轉換規則或插件。
使用 @babel/preset-env 的方式也非常簡單,只需要在 .babelrc 或 babel.config.js 中配置該預設即可,下面是一個綜合案例:
{"presets": [["@babel/preset-env",{// 目標環境設置為最近的兩個瀏覽器版本以及 Safari 7 及以上版本"targets": {"browsers": ["last 2 versions", "safari >= 7"]},// 將 ES6 模塊轉換為 CommonJS 模塊"modules": "commonjs",// 啟用按需加載 polyfill 的功能"useBuiltIns": "usage",// 使用 core-js 3 版本的 polyfill"corejs": 3,// 打印詳細的調試信息"debug": true}]]
}
注意:useBuiltIns 控制了 polyfill 的導入方式,用來配合 @babel/polyfill 使用,值得注意的是,官方不再推薦 Babel > 7.4.0 時使用 @babel/polyfill,可以選擇使用 core-js。
更多配置規則請參考:https://babeljs.io/docs/babel-preset-env
@babel/polyfill
從 Babel 7.4.0 開始,這個包已經被棄用,取而代之的是直接包含 core-js/stable
在本地 node_module 中,可以看到 @babel/polyfill 的依賴包含了 core-js 和 regenerator-runtime,可以認為 polyfill 本身就是 core-js + regenerator-runtime。
從 Babel 7.4.0 開始,我們需要用 core-js 替代 babel-polyfill,而 regenerator-runtime 會在安裝 @babel/runtime 時被依賴安裝,因此不用額外安裝。
babel-runtime
babel-runtime 是一個由 Babel 提供的運行時庫,它包括了一些在編譯過程中需要用到的輔助函數和類,例如 ES6/ES7 語法的 polyfill、generator 函數的處理、Promise 的實現等。
主要功能
babel-runtime 的實現主要功能有兩點:
- 將轉譯中需要的helper 函數從一個模塊中引入,避免重復定義、減小打包體積
下面是一些常見的輔助函數和類的實現:
- classCallCheck:用于實現 ES6 類的構造函數中的類檢查。它會檢查是否使用 new 關鍵字來調用類,并在沒有正確調用的情況下拋出錯誤。
- defineEnumerableProperties:用于定義對象的可枚舉屬性。它接受一個對象和一組屬性描述符,并將這些屬性添加到對象中,并確保它們是可枚舉的。
- extends:用于實現 ES6 類繼承的輔助函數。它會創建一個新的子類,并確保正確設置原型鏈和構造函數。
- asyncToGenerator:用于將 generator 函數轉換為基于 Promise 的異步函數的輔助函數。它接受一個 generator 函數并返回一個新的函數,該函數可以像普通的異步函數一樣被調用。
- regeneratorRuntime:用于支持 generator 函數的運行時庫。它提供了 generator 函數所需的運行時環境,包括狀態機、迭代器和 Promise 的支持。
- 開發類庫/工具時,避免生產污染全局空間的方法。
在一個項目中,我們定義了一個 Array 原型鏈上的方法(比如 Array.includes()),項目依賴 babel-polyfill 實現轉譯。此時,項目引入一個依賴,調用的方法需要使用 Array.includes(),那么在打包時,由于 polyfill 導入于全局環境,就會出現沖突,導致出錯。
解決方案就是用 babel-runtime 處理全局內置對象,將其模塊化,并通過模塊導入的方式引入。
補充
然而 @babel/runtime 沒有支持實例方法,只能通過配置 corejs ,使用 babel/runtime-corejs@x
控制相關 polyfill 的引入,然而 core-js2 的 polyfill 覆蓋范圍相對較小,以下陳列了相關包的區別:
- @babel/polyfill:core-js + regenerator-runtime,Babel 7.4.0后棄用。
- @babel/runtime:Babel 默認的運行時依賴模塊,提供相關 helpers 函數和regenerator-runtime,不包含任何 polyfill 功能。
- @babel/runtime-corejs2:基于 @babel/runtime ,提供了 core-js2 支持部分 polyfill。
- @babel/runtime-corejs3:基于 @babel/runtime ,提供了 core-js3 支持更廣泛的 polyfill。
babel-plugin-transform-runtime
需要注意的是,babel-runtime 只是一個工具庫,需要和 babel-plugin-transform-runtime 配合使用。
babel-plugin-transform-runtime 可以讓 Babel 在編譯過程中, 引用模塊 @babel/runtime提供一些輔助函數和類 ,從而避免在編譯后的代碼中重復出現相同的代碼。
配置
首先,安裝相關包。
npm install --save-dev @babel-plugin-transform-runtime
npm install --save @babel-runtime
其次在 .babelrc 或 babel.config.js 中配置 @babel/plugin-transform-runtime ****插件,corejs 配置項控制是否引入 core-js 或 core-js 的版本。
{"presets": [["@babel/preset-env"],],"plugins": [["@babel/plugin-transform-runtime", {"corejs": false // 可選 false | 2 | 3}]]
}
名稱 | 類型 | 作用 | 使用場景 |
---|---|---|---|
@babel/preset-env | preset | 根據目標環境自動確定需要的轉換規則和 polyfill | 現代 JS 代碼轉換兼容老版本瀏覽器/環境 |
@babel/polyfill (已廢棄) | polyfill | 提供完整的 ES6+ 環境補丁 (包含 core-js 和 regenerator-runtime) | 全局注入 polyfill (Babel 7.4+ 推薦直接使用 core-js 和 regenerator-runtime) |
babel-runtime (舊版本) | 運行時依賴 | 提供編譯時的工具函數和 regenerator-runtime | 配合 transform-runtime 插件減少代碼重復 (已被 @babel/runtime 取代) |
babel-plugin-transform-runtime | 插件 | 自動替換 helper 函數為 runtime 引用,并可配置 core-js 按需 polyfill | 開發庫時避免污染全局環境,減少代碼體積 |
接下來我們可以通過觀察不同 corejs 配置和是否引入 babel-runtime 打包的結果理解一下作用。
示例
控制引入 babel-runtime
轉譯代碼:
class Animal {}
接下來我們通過修改是否啟用 babel-plugin-transform-runtime 控制 babel-runtime 的引入:
// 不引入 babel-runtime
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Animal = /*#__PURE__*/_createClass(function Animal() {_classCallCheck(this, Animal);
});// 引入 babel-runtime,corejs:false
/* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/createClass */ "./node_modules/.pnpm/@babel+runtime@7.23.9/node_modules/@babel/runtime/helpers/esm/createClass.js");
/* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/helpers/classCallCheck */ "./node_modules/.pnpm/@babel+runtime@7.23.9/node_modules/@babel/runtime/helpers/esm/classCallCheck.js");var Animal = /*#__PURE__*/(0,_babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_0__["default"])(function Animal() {(0,_babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_1__["default"])(this, Animal);
});
可以看到 babel-runtime 是通過引入模塊實現 class 的,避免了多文件時定義了多個工具函數,有效減少了打包體積。
控制 corejs 配置
轉譯代碼
new Promise();
string.trimStart()
為體現 corejs 的差異,我們使用兩種實例方法 Promise、String.trimStart() 進行對比。
// corejs:false
new Promise();
string.trimStart();// corejs:2
/* harmony import */ var _babel_runtime_corejs2_core_js_promise__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime-corejs2/core-js/promise */ "./node_modules/.pnpm/@babel+runtime-corejs2@7.23.9/node_modules/@babel/runtime-corejs2/core-js/promise.js");
/* harmony import */ var _babel_runtime_corejs2_core_js_promise__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs2_core_js_promise__WEBPACK_IMPORTED_MODULE_0__);new (_babel_runtime_corejs2_core_js_promise__WEBPACK_IMPORTED_MODULE_0___default())();
string.trimStart();// corejs:3
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime-corejs3/core-js-stable/promise */ "./node_modules/.pnpm/@babel+runtime-corejs3@7.23.9/node_modules/@babel/runtime-corejs3/core-js-stable/promise.js");
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_instance_trim_start__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime-corejs3/core-js-stable/instance/trim-start */ "./node_modules/.pnpm/@babel+runtime-corejs3@7.23.9/node_modules/@babel/runtime-corejs3/core-js-stable/instance/trim-start.js");
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_instance_trim_start__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_core_js_stable_instance_trim_start__WEBPACK_IMPORTED_MODULE_1__);new (_babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_0___default())();
_babel_runtime_corejs3_core_js_stable_instance_trim_start__WEBPACK_IMPORTED_MODULE_1___default()(string).call(string);
依據結果可以看出,
corejs: false 只對ES語法進行了轉換。
corejs:2 為我們的代碼創建了一個沙盒環境,避免了全局空間污染。
corejs: 3 在 corejs: 2的基礎上加入了新的 polyfill 以處理更多的實例方法。
由此總結,對于 Babel < 7.4.0 時,類庫/工具項目應選擇 @babel/runtime,其他項目選擇 @babel/polyfill,當 Babel >= 7.4.0 時,一律使用 @babel/runtime。
如果你覺得這篇文章對你有幫助,請點贊 👍、收藏 👏 并關注我!👀