在前端工程的的打包史中,common文件向來都不是一個好處理的方面。在這一塊,webpack提供了CommonsChunkPlugin來處理這個事情,可是在因爲文檔的模棱兩可,再加上各類配置選項的多樣性和某些bug,仍是有很多坑的。javascript
所謂工欲善其事必先利其器,咱們既然想作common方面的優化,那麼首先確定要知道打包後的文件體積龐大的主要緣由。說到這裏就不得不提到一個至關好用的工具:webpack-bundle-analyzerhtml
它既是一個webpack插件,又是一個命令行工具。可以將webpack包的內容轉換成可縮放的樹狀圖,方便進行交互分析。恩。。。就是這玩意:前端
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兩個配置。
其中第一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 如何決定模塊被打包到哪裏的算法有更爲細緻的控制, 這個配置就會很是有用。
下面的內容是官網弄過來的,其實我也看不太懂。。。
若是設置爲
true
,一個異步的 公共chunk 會做爲options.name
的子模塊,和options.chunks
的兄弟模塊被建立。它會與options.chunks
並行被加載。能夠經過提供想要的字符串,而不是true
來對輸出的文件進行更換名稱。
仍是先看打包分析的結果吧:
經過上面分析能夠看到:
common合理劃分,抓大放小。
antd和echarts提取出來,並行加載。
components 用到什麼打包什麼。(手動控制的)
大大減少了其餘業務包的體積,93%的業務包大小控制在25K之內,剩下7%的業務包大小控制在50k之內。(開啓gzip)
首屏加載資源的總大小几乎沒有變化。
echarts 和Ueditor運行時按需加載。
tree-shaking
的探索
Vendor and code splitting in webpack 2
webpack 按需打包加載
weboack Split app and vendor code
awesome-webpack-cn