Scope Hoisting 可以讓 Webpack 打包出來的程式碼檔案更小、執行的更快, 它又譯作 "作用域提升",是在 Webpack3 中新推出的功能。 單從名字上看不出 Scope Hoisting 到底做了什麼,下面來詳細介紹它。
認識 Scope Hoisting
讓我們先來看看在沒有 Scope Hoisting 之前 Webpack 的打包方式。
假如現在有兩個檔案分別是 util.js
:
export default 'Hello,Webpack';
複製程式碼
和入口檔案 main.js
:
import str from './util.js';
console.log(str);
複製程式碼
以上原始碼用 Webpack 打包後輸出中的部分程式碼如下:
[
(function (module, __webpack_exports__, __webpack_require__) {
var __WEBPACK_IMPORTED_MODULE_0__util_js__ = __webpack_require__(1);
console.log(__WEBPACK_IMPORTED_MODULE_0__util_js__["a"]);
}),
(function (module, __webpack_exports__, __webpack_require__) {
__webpack_exports__["a"] = ('Hello,Webpack');
})
]
複製程式碼
在開啟 Scope Hoisting 後,同樣的原始碼輸出的部分程式碼如下:
[
(function (module, __webpack_exports__, __webpack_require__) {
var util = ('Hello,Webpack');
console.log(util);
})
]
複製程式碼
從中可以看出開啟 Scope Hoisting 後,函式申明由兩個變成了一個,util.js
中定義的內容被直接注入到了 main.js
對應的模組中。
這樣做的好處是:
- 程式碼體積更小,因為函式申明語句會產生大量程式碼;
- 程式碼在執行時因為建立的函式作用域更少了,記憶體開銷也隨之變小。
Scope Hoisting 的實現原理其實很簡單:分析出模組之間的依賴關係,儘可能的把打散的模組合併到一個函式中去,但前提是不能造成程式碼冗餘。 因此只有那些被引用了一次的模組才能被合併。
由於 Scope Hoisting 需要分析出模組之間的依賴關係,因此原始碼必須採用 ES6 模組化語句,不然它將無法生效。 原因和4-10 使用 TreeShaking 中介紹的類似。
使用 Scope Hoisting
要在 Webpack 中使用 Scope Hoisting 非常簡單,因為這是 Webpack 內建的功能,只需要配置一個外掛,相關程式碼如下:
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
plugins: [
// 開啟 Scope Hoisting
new ModuleConcatenationPlugin(),
],
};
複製程式碼
同時,考慮到 Scope Hoisting 依賴原始碼需採用 ES6 模組化語法,還需要配置 mainFields
。
原因在 4-10 使用 TreeShaking 中提到過:因為大部分 Npm 中的第三方庫採用了 CommonJS 語法,但部分庫會同時提供 ES6 模組化的程式碼,為了充分發揮
Scope Hoisting 的作用,需要增加以下配置:
module.exports = {
resolve: {
// 針對 Npm 中的第三方模組優先採用 jsnext:main 中指向的 ES6 模組化語法的檔案
mainFields: ['jsnext:main', 'browser', 'main']
},
};
複製程式碼
對於採用了非 ES6 模組化語法的程式碼,Webpack 會降級處理不使用 Scope Hoisting 優化,為了知道 Webpack 對哪些程式碼做了降級處理,
你可以在啟動 Webpack 時帶上 --display-optimization-bailout
引數,這樣在輸出日誌中就會包含類似如下的日誌:
[0] ./main.js + 1 modules 80 bytes {0} [built]
ModuleConcatenation bailout: Module is not an ECMAScript module
複製程式碼
其中的 ModuleConcatenation bailout
告訴了你哪個檔案因為什麼原因導致了降級處理。
也就是說要開啟 Scope Hoisting 併發揮最大作用的配置如下:
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
resolve: {
// 針對 Npm 中的第三方模組優先採用 jsnext:main 中指向的 ES6 模組化語法的檔案
mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
// 開啟 Scope Hoisting
new ModuleConcatenationPlugin(),
],
};
複製程式碼
本例項提供專案完整程式碼