webpack 搖樹處理

garynan發表於2019-03-18

webpack 搖樹處理 Tree Shaking

GitHub 學習 Demo。

搖樹處理 Tree Shaking 是一項在消除 Javascript 上下文中死程式碼的技術。 它依賴於ES2015模組語法的靜態結構,即 importexport。這個專業名詞被 ES2015 module bundler rollup 所彙總推廣了。

webpack 2版本自帶內建支援ES2015模組(alias harmony modules 別名和聲模組)以及未使用的模組匯出檢測。

新的webpack 4版本擴充套件了這一功能,通過 package.jsonsideEffects 屬性向編譯器提供提示,表示專案中的哪些檔案是“pure 純粹的”,因此如果未使用則可以安全修剪。

修改專案

webpack-demo
|- package.json
|- webpack.config.js
|- /dist
  |- bundle.js
  |- index.html
|- /src
  |- index.js
+ |- math.js
|- /node_modules

# src/math.js

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}

# webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
- }
+ },
+ mode: 'development',
+ optimization: {
+   usedExports: true
+ }
};

# src/index.js

- import _ from 'lodash';
+ import { cube } from './math.js';

  function component() {
-   var element = document.createElement('div');
+   var element = document.createElement('pre');

-   // Lodash, now imported by this script
-   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+   element.innerHTML = [
+     'Hello webpack!',
+     '5 cubed is equal to ' + cube(5)
+   ].join('\n\n');

    return element;
  }

  document.body.appendChild(component());
複製程式碼

記住,現在並沒有 import square 方法。 這段程式碼就是死程式碼, 沒有沒使用的 export 程式碼應該被裁剪掉.現在執行 npm run build, 並檢查 不能 bundle 檔案:

# dist/bundle.js (around lines 90 - 100)

/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
  'use strict';
  /* unused harmony export square */
  /* harmony export (immutable) */ __webpack_exports__['a'] = cube;
  function square(x) {
    return x * x;
  }

  function cube(x) {
    return x * x * x;
  }
});
複製程式碼

你會注意到沒有 imprtsquare 方法,仍然被捆綁在bundle中,並標有 unused harmony export square 備註程式碼未被匯入。我們將在下一節中解決這個問題。

設定 sideEffects 選項

在 100% 的 ESM 模組世界中,識別副作用很簡單。 但是,我們還沒有,所以在同時,有必要為 webpack 的編譯器提供關於程式碼“pureness 純度”的提示。

方法是,向 package.json 新增 sideEffects 屬性:

# package.json

{
  "name": "your-project",
  "sideEffects": false
}
複製程式碼

上面提到的所有程式碼都不包含副作用,因此我們可以簡單地將該屬性標記為 false ,以通知 webpack 它可以安全地修剪未使用的匯出。

tips:
“副作用”的定義是,在匯入時執行特殊行為的程式碼,而不是公開一個或多個匯出。 一個例子是 polyfill ,它影響全域性範圍,通常不提供匯出。

如果您的程式碼確實有一些副作用,可以提供一個陣列:

# package.json

{
  "name": "your-project",
  "sideEffects": [
    "./src/some-side-effectful-file.js"
  ]
}
複製程式碼

這個引數接受相對,絕對路徑,或全域性變數的數字作為鍵值。使用的方式是 micromatch

請注意,任何匯入的檔案都會受到樹抖動的影響。 這意味著如果在專案中使用類似css-loader的東西並匯入 CSS 檔案,則需要將其新增到副作用列表中,這樣就不會在生產模式下無意中丟失程式碼:

# package.json

{
  "name": "your-project",
  "sideEffects": [
    "./src/some-side-effectful-file.js",
    "*.css"
  ]
}
複製程式碼

最後,"sideEffects" 也可以設定在 module.rules 的選項中。

最小化輸出 Minify the Output

所以我們通過使用importexport語法來提取我們的“死程式碼”,但我們仍然需要從捆綁中刪除它。 為此,請將模式配置選項設定為生產配置選項。

# webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
- mode: 'development',
- optimization: {
-   usedExports: true
- }
+ mode: 'production'
};
複製程式碼

我們可以執行npm run build,看看是否有任何變化。

注意 dist/bundle.js 有什麼不同嗎?
很明顯,整個捆綁包現在縮小並混淆了,但是,如果你仔細觀察,你將看不到包 含 原 squre 方法,但會看到一個混淆版本的squre 方法(funtion r(e){return e * e * e}NA = R)。 隨著縮小和樹搖動,我們的捆綁現在變小了幾個位元組! 雖然在這個人為的例子中看起來似乎並不多,但是當處理具有複雜依賴樹的較大應用程式時,效果將更加的明顯。

tips:
樹搖動工作需要 ModuleConcatenationPlugin,可以通過把 mode 切換為 production 生效。
也可以手動新增 ModuleConcatenationPlugin

總結

要想使用 tree shaking, 你必須:

  • 使用 ES2015 module 語法 importexport
  • 確保沒有編譯器將您的 ES2015 模組語法轉換為 CommonJS 模組,(這是流行的 Babel 預設 @babel/preset-env 的預設行為, 有關更多詳細資訊,請參閱文件
  • 向 package.json 新增 sideEffects 屬性。
  • 使用生產模式配置選項啟用各種優化(mode: "production"),包括縮小和樹抖動。

如果您對更多優化輸出的方法感興趣,請跳至下一個指南,瞭解有關生產的詳細資訊

相關文章