webpack的CommonsChunkPlugin分析與優化

前言

在前端工程的的打包史中,common文件向來都不是一個好處理的方面。在這一塊,webpack提供了CommonsChunkPlugin來處理這個事情,可是在因爲文檔的模棱兩可,再加上各類配置選項的多樣性和某些bug,仍是有很多坑的。javascript

分析包

所謂工欲善其事必先利其器,咱們既然想作common方面的優化,那麼首先確定要知道打包後的文件體積龐大的主要緣由。說到這裏就不得不提到一個至關好用的工具:webpack-bundle-analyzerhtml

它既是一個webpack插件,又是一個命令行工具。可以將webpack包的內容轉換成可縮放的樹狀圖,方便進行交互分析。恩。。。就是這玩意:
image前端

安裝

npm install --save-dev webpack-bundle-analyzer

做爲插件使用

webpack.config.js中:java

var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

// ...
plugins: [new BundleAnalyzerPlugin()]
// ...

默認配置以下:node

new BundleAnalyzerPlugin({
  // Can be `server`, `static` or `disabled`.
  // In `server` mode analyzer will start HTTP server to show bundle report.
  // In `static` mode single HTML file with bundle report will be generated.
  // In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`.
  analyzerMode: 'server',
  // Host that will be used in `server` mode to start HTTP server.
  analyzerHost: '127.0.0.1',
  // Port that will be used in `server` mode to start HTTP server.
  analyzerPort: 8888,
  // Path to bundle report file that will be generated in `static` mode.
  // Relative to bundles output directory.
  reportFilename: 'report.html',
  // Automatically open report in default browser
  openAnalyzer: true,
  // If `true`, Webpack Stats JSON file will be generated in bundles output directory
  generateStatsFile: false,
  // Name of Webpack Stats JSON file that will be generated if `generateStatsFile` is `true`.
  // Relative to bundles output directory.
  statsFilename: 'stats.json',
  // Options for `stats.toJson()` method.
  // For example you can exclude sources of your modules from stats file with `source: false` option.
  // See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
  statsOptions: null,
  // Log level. Can be 'info', 'warn', 'error' or 'silent'.
  logLevel: 'info'
})

命令行使用

第一步:webpack

webpack --profile --json > stats.json

第二步:git

webpack --profile --json | Out-file 'stats.json' -Encoding OEM

執行成功後,你將看到如下動態網頁:github

圖片描述

在這裏順便放上線上文件加載waterfall,做爲對比。
圖片描述web

問題

經過圖表能夠看到,在如下配置下:算法

config.plugins.push(new CommonsChunkPlugin('commons', 'js/commons.[hash].bundle.js'));

打包出來的文件仍是有不少問題的:

  • common包規劃不合理, swiper.js ,area.json等公用文件大量重複加載。

  • antd 沒有抽離出來,沒法並行加載,也沒法進一步作運行時按需加載。

  • echarts在每一個使用的包都單獨打包一份,只要包含echarts的包,基本一百多kb。(線上壓縮並開啓gzip)

  • import {ImgBigSwiper} from 'components/src/index'; 這種寫法會致使將components裏面的全部組件所有打包進頁面的js。應該這樣寫:import ImgBigSwiper from 'components/src/ImgBigSwiper';挨個引入,見webpack將ES6編譯成CommonJs後只引入用到的模塊

  • common.js獨佔490kb,要等這個包加載完後index纔開始解析路由。

在這個過程當中,會發現一個有趣的事情。就是index.html頁面的script加載分爲如下兩個部分:

......
<script type="text/javascript" src="//res.dinghuo123.com/src/common/ueditor/ueditor.config.js"></script>
<script type="text/javascript" src="//res.dinghuo123.com/src/common/ueditor/ueditor.config.js"></script>
<script type="text/javascript" src="//res.dinghuo123.com/src/common/ueditor/ueditor.config.js"></script>
......
<script>
    document.write('<script src="https://resource.dinghuo123.com/dist/ydhv2/webpack.assets.js?v=' + Math.random() + '"><\/script>');
    document.write('<script src="' + window.WEBPACK_ASSETS['commons'].js + '"><\/script>');
    document.write('<script src="//res.dinghuo123.com/src/common/ueditor/ueditor.config.js"><\/script>');
    document.write('<script src="' + window.WEBPACK_ASSETS['index'].js + '"><\/script>');
</script>

而後你會發現,是上面一塊的script並行加載完,才並行加載下一個script標籤的內容。你們能夠思考一下爲何。

改進配置

進過調整以後的CommonsChunkPlugin配置:

config.plugins.push(new CommonsChunkPlugin({
  name: 'commons',
  minChunks: Infinity // 隨着 入口chunk 愈來愈多,這個配置保證沒其它的模塊會打包進 公共chunk
}));

config.plugins.push(new CommonsChunkPlugin({
  async:'antd',
  minChunks(module) {
    var context = module.context;
    return context && context.indexOf('antd/dist') >= 0;
  }
}));

config.plugins.push(new CommonsChunkPlugin({
  async:'echarts',
  minChunks(module) {
    var context = module.context;
    return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0);
  }
}));

這裏用到了minChunks和async兩個配置。

minChunks

其中第一name的commons是一個entry入口,裏面是一個依賴包的數組。minChunks設置爲Infinity這個配置保證沒其它的模塊會打包進 公共chunk。由於說實話,CommonsChunkPlugin的commons分析實在是不怎麼只能,仍是手動控制會更好一些。
固然,你能夠傳入一個 function ,以添加定製的邏輯(默認是 chunk 的數量),這個函數會被 CommonsChunkPlugin 插件回調,而且調用函數時會傳入 module 和 count 參數。
module 參數表明每一個 chunks 裏的模塊,這些 chunks 是你經過 name/names 參數傳入的。

  • module.context: The directory that stores the file. For example: '/my_project/node_modules/example-dependency'

  • module.resource: The name of the file being processed. For example: '/my_project/node_modules/example-dependency/index.js'

  • count 參數表示 module 被使用的 chunk 數量。

當你想要對 CommonsChunk 如何決定模塊被打包到哪裏的算法有更爲細緻的控制, 這個配置就會很是有用。

async

下面的內容是官網弄過來的,其實我也看不太懂。。。

若是設置爲 true,一個異步的 公共chunk 會做爲 options.name 的子模塊,和 options.chunks 的兄弟模塊被建立。它會與 options.chunks 並行被加載。能夠經過提供想要的字符串,而不是 true 來對輸出的文件進行更換名稱。

結果

仍是先看打包分析的結果吧:
圖片描述

經過上面分析能夠看到:

  1. common合理劃分,抓大放小。

  2. antd和echarts提取出來,並行加載。

  3. components 用到什麼打包什麼。(手動控制的)

  4. 大大減少了其餘業務包的體積,93%的業務包大小控制在25K之內,剩下7%的業務包大小控制在50k之內。(開啓gzip)

  5. 首屏加載資源的總大小几乎沒有變化。

接下來的方向

  1. echarts 和Ueditor運行時按需加載。

  2. tree-shaking的探索

參考

Vendor and code splitting in webpack 2
webpack 按需打包加載
weboack Split app and vendor code
awesome-webpack-cn

相關文章
相關標籤/搜索