webpack 搖樹處理 Tree Shaking
搖樹處理 Tree Shaking 是一項在消除 Javascript 上下文中死程式碼的技術。 它依賴於ES2015模組語法的靜態結構,即 import
和 export
。這個專業名詞被 ES2015 module bundler rollup 所彙總推廣了。
webpack 2版本自帶內建支援ES2015模組(alias harmony modules 別名和聲模組)以及未使用的模組匯出檢測。
新的webpack 4版本擴充套件了這一功能,通過 package.json
的 sideEffects
屬性向編譯器提供提示,表示專案中的哪些檔案是“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;
}
});
複製程式碼
你會注意到沒有 imprt
的 square
方法,仍然被捆綁在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
所以我們通過使用import
和export
語法來提取我們的“死程式碼”,但我們仍然需要從捆綁中刪除它。 為此,請將模式配置選項設定為生產配置選項。
# 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 語法
import
和export
。 - 確保沒有編譯器將您的 ES2015 模組語法轉換為 CommonJS 模組,(這是流行的
Babel
預設@babel/preset-env
的預設行為, 有關更多詳細資訊,請參閱文件。 - 向 package.json 新增 sideEffects 屬性。
- 使用生產模式配置選項啟用各種優化(mode: "production"),包括縮小和樹抖動。
如果您對更多優化輸出的方法感興趣,請跳至下一個指南,瞭解有關生產的詳細資訊。