webpack 版本:4.35.0前端
webpack-cli 版本:3.3.5node
在當前的前端項目中,經常使用 gulp、webpack、Browserify 等將多個文件壓縮合併成一個。這個過程稱爲打包。 打包是一個將文件引入併合併到一個單獨文件的過程,最終造成一個 「bundle」。接着在頁面上引入該 bundle,整個應用便可一次性加載。webpack
打包是個很是棒的技術,但隨着你的應用增加,你的代碼包也將隨之增加。尤爲是在整合了體積巨大的第三方庫的狀況下。你須要關注你代碼包中所包含的代碼,以免因體積過大而致使加載時間過長。web
爲了不搞出大致積的代碼包,在前期就思考該問題並對代碼包進行分割是個不錯的選擇。代碼分割是由諸如 Webpack(代碼分割)和 Browserify(factor-bundle)這類打包器支持的一項技術,可以建立多個包並在運行時動態加載。算法
對應用進行代碼分割可以幫助你「懶加載」當前用戶所須要的內容,可以顯著地提升你的應用性能。儘管並無減小應用總體的代碼體積,但你能夠避免加載用戶永遠不須要的代碼,並在初始加載的時候減小所需加載的代碼量。gulp
打包解決了項目中依賴文件過多,而致使 http 請求過多的問題瀏覽器
代碼分割則是解決打包的反作用——單文件過大,致使加載時間過長。緩存
所以代碼分割的關鍵點在於,如何控制分割粒度:bash
有三種經常使用的代碼分離方法:babel
entry
配置手動地分離代碼。每一個 entry
都會在最後的打包中輸出一個對應的文件。所以能夠顯示地進行代碼分割:
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
// ...
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, './dist')
}
}
複製代碼
進行打包構建後會在 dist
文件夾輸出:
app.f7badf83cc90de106d41.js
print.e00bba994e917cc7fc59.js
複製代碼
動態導入也能夠達到代碼分割的效果:
import _ from 'lodash';
function component() {
var element = document.createElement('div');
// Lodash(目前經過一個 script 腳本引入)對於執行這一行是必需的
element.innerHTML = _.join(['你好呀', 'webpack'], ' ');
element.onclick = (e) => import(/* webpackChunkName: "print" */ './print').then((Print) => {
})
return element;
}
document.body.appendChild(component());
複製代碼
webpackChunkName
設置了輸出包的名字。
module.exports = {
entry: {
app: './src/index.js'
},
// ...
output: {
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].js',
path: path.resolve(__dirname, './dist')
}
}
複製代碼
output
的 chunkFilename
決定了非入口(non-entry) chunk 文件的名稱。這裏的 chunk 是經過代碼分割產生的,所以這裏經過此選項來設置文件的名字。
運行打包:
app.73efde30ee057cb8c817.js
print.e4408db3b4e5072295de.js
複製代碼
在 webpack 4 以前,使用 CommonsChunkPlugin
進行公共 chunk 抽取。
module.exports = {
entry: './src/index.js',
entry: {
main: './src/index.js',
vendor: [
'lodash'
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Caching'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
})
],
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
}
};
複製代碼
這裏有兩點值得注意:
entry
中的 vendor
:顯式引用 lodash
,因爲 index.js
也導入了 lodash
,所以它就變成了一個公共的模塊。new webpack.optimize.CommonsChunkPlugin
抽取這些公共模塊webpack 4 以後 CommonsChunkPlugin
被棄用了,使用 SplitChunksPlugin
代替。 SplitChunksPlugin
是開箱即用的,只須要在 module.exports
中進行配置:
module.exports = {
//...
optimization: {
splitChunks: { // 該選項將用於配置 SplitChunksPlugin 插件
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial',
minChunks: 2
}
}
}
},
output: {
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].js',
path: path.resolve(__dirname, './dist')
}
};
複製代碼
上面的配置會將 node_modules
下引用的全部模塊都使用 vendor
這個做爲 chunk 名以 output
中 chunkFilename
設置的格式打包到 dist
文件夾中。
SplitChunksPlugin
的選項:
cacheGroups 中每一項都接收以下參數:
filename: 重寫 chunk 命名方式
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
filename:'[name].bundle.js'
chunks: 'initial',
minChunks: 2,
}
複製代碼
最後會生成名字爲 vendor.bundle.js
的文件
關於 SplitChunksPlugin 插件的配置,請參見 SplitChunksPlugin 解析
代碼分割的關鍵點在於控制分割粒度:
若是在不考慮大小的狀況下,最後會打包出包含全部代碼的單個文件。咱們能夠 optimization.splitChunks.maxSize
來控制 chunk 的大小以達到分割成多文件的目的。
只經過大小來控制代碼分割擁有以下弊端:
入口文件大小未獲得最大的優化
不能使用緩存。
在項目中,有些引入的工具的更新頻率是很是低的,這時候
表現和結構未進行分離。
瀏覽器進行渲染的時候,表現和結構是基於不一樣的算法進行解析的。而且,樣式代碼也會增長請求的響應時間。
在項目中,經常會引入一些三方的工具庫或者 polyfill 等,好比 lodash
,babel-polyfill
。這些代碼在項目中改動不多,所以建議將這些代碼提取到一個單獨的文件中。
這樣作有幾個好處:
在項目中可使用 ExtractTextWebpackPlugin 插件將 CSS 文件抽取到單獨的 bundle 中。
entry: {
polyfill: './src/utils/polyfill.js',
main: './src/main.js',
app: './src/index.js'
},
複製代碼