通過Scope Hoisting優化Webpack輸出

WyattFelici 6年前發布 | 29K 次閱讀 前端技術 Webpack,前端技術

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(),
  ],
};

 

 

來自:https://segmentfault.com/a/1190000012600832

 

 本文由用戶 WyattFelici 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!