webpack 把我們所有的文件都打包成一個(gè) JS 文件,這樣即使你是小項(xiàng)目茫叭,打包后的文件也會(huì)非常大酬屉。下面就來講下如何從多個(gè)方面進(jìn)行優(yōu)化。
去除不必要的插件
剛開始用 webpack 的時(shí)候揍愁,開發(fā)環(huán)境和生產(chǎn)環(huán)境用的是同一個(gè) webpack 配置文件呐萨,導(dǎo)致生產(chǎn)環(huán)境打包的 JS 文件包含了一大堆沒必要的插件,比如 HotModuleReplacementPlugin
, NoErrorsPlugin
... 這時(shí)候不管用什么優(yōu)化方式莽囤,都沒多大效果浩聋。所以储矩,如果你打包后的文件非常大的話,先檢查下是不是包含了這些插件。
提取第三方庫
像 react 這個(gè)庫的核心代碼就有 627 KB衫冻,這樣和我們的源代碼放在一起打包卖宠,體積肯定會(huì)很大孩灯。所以可以在 webpack 中設(shè)置
{
entry: {
bundle: 'app'
vendor: ['react']
}
plugins: {
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js')
}
}
這樣打包之后就會(huì)多出一個(gè) vendor.js
文件潦俺,之后在引入我們自己的代碼之前,都要先引入這個(gè)文件狼牺。比如在 index.html
中
<script src="/build/vendor.js"></script>
<script src="/build/bundle.js"></script>
除了這種方式之外羡儿,還可以通過引用外部文件的方式引入第三方庫,比如像下面的配置
{
externals: {
'react': 'React'
}
}
externals
對(duì)象的 key 是給 require
時(shí)用的是钥,比如 require('react')
掠归,對(duì)象的 value 表示的是如何在 global 中訪問到該對(duì)象,這里是 window.React
悄泥。這時(shí)候 index.html
就變成下面這樣
<script src="http://cdn.bootcss.com/react/0.14.7/react.min.js"></script>
<script src="/build/bundle.js"></script>
當(dāng)然虏冻,個(gè)人更推薦第一種方式。
目前推薦用 DLL 的方式提取第三方庫弹囚。
代碼壓縮
webpack 自帶了一個(gè)壓縮插件 UglifyJsPlugin厨相,只需要在配置文件中引入即可。
{
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
}
加入了這個(gè)插件之后鸥鹉,編譯的速度會(huì)明顯變慢蛮穿,所以一般只在生產(chǎn)環(huán)境啟用。
另外毁渗,服務(wù)器端還可以開啟 gzip 壓縮践磅,優(yōu)化的效果更明顯。
代碼分割
什么是代碼分割呢灸异?我們知道府适,一般加載一個(gè)網(wǎng)頁都會(huì)把全部的 js 代碼都加載下來幻碱。但是對(duì)于 web app 來說,我們更想要的是只加載當(dāng)前 UI 的代碼细溅,沒有點(diǎn)擊的部分不加載。
看起來好像挺麻煩儡嘶,但是通過 webpack 的 code split 以及配合 react router 就可以方便實(shí)現(xiàn)喇聊。具體的例子可以看下 react router 的官方示例 huge apps。不過這里還是講下之前配置踩過的坑蹦狂。
code split
是不支持 ES6 的模塊系統(tǒng)的誓篱,所以在導(dǎo)入和導(dǎo)出的時(shí)候千萬要注意,特別是導(dǎo)出凯楔。如果你導(dǎo)出組件的時(shí)候用 ES6 的方式窜骄,這時(shí)候不管導(dǎo)入是用 CommomJs 還是 AMD,都會(huì)失敗摆屯,而且還不會(huì)報(bào)錯(cuò)邻遏!
當(dāng)然會(huì)踩到這個(gè)坑也是因?yàn)槲覄倓偛庞?NodeJS,而且一入門就是用 ES6 的風(fēng)格虐骑。除了這個(gè)之外准验,還有一點(diǎn)也要注意,在生產(chǎn)環(huán)境的 webpack 配置文件中廷没,要加上 publicPath
output: {
path: xxx,
publicPath: yyy,
filename: 'bundle.js'
}
不然的話糊饱,webpack 在加載 chunk 的時(shí)候,路徑會(huì)出錯(cuò)颠黎。
設(shè)置緩存
開始這個(gè)小節(jié)之前另锋,可以先看下大神的一篇文章:大公司里怎樣開發(fā)和部署前端代碼。
對(duì)于靜態(tài)文件狭归,第一次獲取之后夭坪,文件內(nèi)容沒改變的話,瀏覽器直接讀取緩存文件即可过椎。那如果緩存設(shè)置過長台舱,文件要更新怎么辦呢?嗯潭流,以文件內(nèi)容的 MD5 作為文件名就是一個(gè)不錯(cuò)的解決方案竞惋。來看下用 webpack 應(yīng)該怎樣實(shí)現(xiàn)
output: {
path: xxx,
publicPath: yyy,
filename: '[name]-[chunkhash:6].js'
}
打包后的文件名加入了 hash 值
const bundler = webpack(config)
bundler.run((err, stats) => {
let assets = stats.toJson().assets
let name
for (let i = 0; i < assets.length; i++) {
if (assets[i].name.startsWith('main')) {
name = assets[i].name
break
}
}
fs.stat(config.buildTemplatePath, (err, stats) => {
if (err) {
fs.mkdirSync(config.buildTemplatePath)
}
writeTemplate(name)
})
})
手動(dòng)調(diào)用 webpack 的 API,獲取打包后的文件名灰嫉,通過 writeTemplate
更新 html 代碼拆宛。完整代碼猛戳 gitst。
這樣子讼撒,我們就可以把文件的緩存設(shè)置得很長浑厚,而不用擔(dān)心更新問題股耽。