1. 程式人生 > >webpack學習之路(六)

webpack學習之路(六)

Tree Shaking

ps:官方文件中本節目錄結構繼承於起步。

Tree Shaking是一個常用於移除js環境中無用程式碼的術語,它依賴於ES2015模組系統中的靜態結構特性比如import和export。它的名字和概念其實興起於ES2015打包工具rollup。

webpack發行後就內建支援ES2015模組和無用模組匯出檢測。而webpack4又擴充套件了這個功能:通過package.js的"side effect"屬性來提示編譯器專案中哪些檔案是純粹的(可以放心地刪除)。

新增通用模組

新增一個匯出兩個函式的通用模組:src/math.js。

project

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; }

把mode設定為開發模式來防止輸出包被壓縮:

webpack.config.js

const path = require('path');

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

在入口指令碼中引入其中一個方法,為了簡潔刪除loadash:

src/index.js

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

  function component() {
-   var element = document.createElement('div');
+ var element = document.createElement('pre'); - // lodash 是由當前 script 指令碼 import 匯入進來的 - 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());

現在我們沒有從src/math.js匯入square方法,這個方法就成了未引用程式碼,也意味著應該被刪除。現在構建一下專案觀察一下輸出包:

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; }

留意一下這個"unused harmony export square"註釋,繼續看一下往下的程式碼你會發現它沒有被引用但是輸出包裡面依然有這個方法,接下來我們會解決這個問題。

將檔案標記為無副作用

ps:副作用指的是一個模組被匯入時會有一些特殊的行為而不只是暴露出一個或多個export,比如polyfill,它會影響全域性作用域而且不提供export。

在一個純粹的ESM模組世界中,識別副作用很簡單,但是我們沒法達到這種程度,所以給webpack編譯器一些關於你的程式碼的純淨度的提示是很有必要的。

解決方案就是package.json的"sideEffects"屬性:

{
  "name": "your-project", "sideEffects": false }

之前的程式碼都沒有什麼副作用所以我們可以簡單地把屬性設定為false來告訴webpack可以安全地刪除所有的無用匯出模組。

但如果你的程式碼有一些副作用那你可以提供一個數組:

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

這個陣列支援相關檔案的相對路徑、絕對路徑和glob模式。它內部使用micromatch。

ps:任何匯入的檔案都會受到tree shaking的影響,也就是說如果你在專案中使用了css-loader匯入了css檔案,那這些檔案也需要新增到tree shaking列表否則它就會在生產模式中被無意刪掉。

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

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

壓縮輸出:

如上我們已經可以使用import和export找出那些需要被刪除的無用程式碼,但是我們還需要在輸出包中刪掉它們。為此我們把mode設定為生產模式就可以壓縮輸出了:

webpack.config.js

const path = require('path');

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

ps:也可以在命令列中使用--optimize--minimize標記,它會在內部呼叫壓縮外掛UglifyJsPlugin。

現在構建一下專案觀察一下結果:現在整個輸出包都已經被精簡過了,square函式沒有被引入而且cube函式也被精簡過了(function r(e){return e*e*e}n.a=r)。經過tree shaking和輸出壓縮輸出包小了幾個位元組,雖然這不算多但是在大型專案中會起到可觀的效果。

結論

為了學會使用 tree shaking,你必須……

  • 使用 ES2015 模組語法(即 import 和 export)。
  • 在專案 package.json 檔案中,新增一個 "sideEffects" 入口。
  • 引入一個能夠刪除未引用程式碼(dead code)的壓縮工具(minifier)(例如 UglifyJSPlugin)。

你可以將應用程式想象成一棵樹。綠色表示實際用到的原始碼和 library,是樹上活的樹葉。灰色表示無用的程式碼,是秋天樹上枯萎的樹葉。為了除去死去的樹葉,你必須搖動這棵樹,使它們落下。