webpack進(jìn)階——緩存與獨(dú)立打包的用法
本文介紹了webpack進(jìn)階——緩存與獨(dú)立打包的用法,分享給大家,希望對(duì)大家有幫助
先來看看最基礎(chǔ)的webpack配置:
var path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
在index.js中引入了lodash庫:
src/index.js:
import _ from 'lodash';
function component() {
var element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
document.body.appendChild(component());
打包之后,只會(huì)生成一個(gè)bundle.js,這樣的話,每次若要加載資源文件,瀏覽器都會(huì)加載根本不會(huì)改動(dòng)的lodash庫,這樣很低效。
由于如果每次去訪問瀏覽器,瀏覽器都重新下載資源,由于網(wǎng)絡(luò)獲取資源可能很慢,可能頁面久久加載不出來,低效且不友好,故瀏覽器會(huì)緩存資源,以避免每次訪問都通過網(wǎng)絡(luò)去獲取資源。
但是,由于瀏覽器緩存,又會(huì)出現(xiàn)新的問題,如果我們部署版本時(shí)不更改資源的文件名,瀏覽器可能認(rèn)為它沒有更新,就會(huì)使用它的緩存版本。
這樣我們就需要解決兩個(gè)問題:第一,分離打包文件。第二,解決緩存問題。
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
common: ['lodash'],
app: './src/index.js'
},
output: {
filename: '[name].[hash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // 指代index.js引入的lodash庫
})
]
}
主要變動(dòng):
- 添加插件:CommonsChunkPlugin,提取引入的庫,并且更名,實(shí)現(xiàn)代碼分離。
- 輸出上在名字上加了hash,每次打包后,hash值都不一樣解決了瀏覽器緩存的問題。
結(jié)果:index.js打包為app.[hash].js,index.js引入的lodash打包為common.[hash].js。這樣解決了瀏覽器緩存問題和實(shí)現(xiàn)了靜態(tài)資源代碼和源代碼的分離,但是新的問題又出現(xiàn)了。
第一次打包后(注意Asset列下的名字):

每次我們修改源代碼時(shí),再次打包,不僅僅index生成app.[hash].js的hash值發(fā)生了變化,

而且common.[hash].js的hash值與app的hash值相同也發(fā)生了變化(可以自行測(cè)試一下,先webpack打包一次,修改index.js后再次打包一次)。
這并不是我們想要的結(jié)果,雖然源代碼hash改變解決了瀏覽器使用緩存版本的問題,但是,如果common.js的hash值也一同發(fā)生了變化的話,那么瀏覽器也還需要每次都請(qǐng)求不會(huì)發(fā)生改變的靜態(tài)代碼common,這樣還是浪費(fèi)了網(wǎng)絡(luò)資源,很低效。
注:本案例會(huì)多次打包,dist目錄中會(huì)生成過多垃圾文件,在實(shí)際使用中都使用了CleanWebpackPlugin插件。
new CleanWebpackPlugin(['dist']) // 加入在插件數(shù)組中,用于在每次打包前,都清空打包文件夾下之前打包的文件。
如果修改了index,僅僅只是生成的app的hash值發(fā)生變化,而common的hash值不發(fā)生變化,那就能夠達(dá)到我們的目的,既能緩存庫又能識(shí)別源文件的更改。
我們進(jìn)行如下配置: output中將 [name].[hash].js 改為[name].[chunkhash].js ,讓每個(gè)文件生成唯一的hash值:
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
common: ['lodash'],
app: './src/index.js'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(['dist']),
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // 指代index.js引入的lodash庫
})
]
}
(注意:不要在開發(fā)環(huán)境下使用 [chunkhash],因?yàn)檫@會(huì)增加編譯時(shí)間。將開發(fā)和生產(chǎn)模式的配置分開,并在開發(fā)模式中使用 [name].js 的文件名,在生產(chǎn)模式中使用 [name].[chunkhash].js 文件名,所以如果這個(gè)時(shí)候使用了熱替換插HotModuleReplacementPlugin,將會(huì)導(dǎo)致編譯不成功?。?br />
我們配置好之后,進(jìn)行webpack打包:

chunkhash是根據(jù)文件內(nèi)容生成的hash,可見app與common生成的hash值不相同了(對(duì)比使用 [name].[hash].js打包)。
我們?cè)趇ndex.js中隨便進(jìn)行修改,再次打包:

奇怪的是,雖然common與app生成了單獨(dú)的hash值,但是修改了index.js,common的hash值還是發(fā)生了變化。
原因是:為了最小化生成的文件大小,webpack使用標(biāo)識(shí)符而不是模塊名稱,在編譯期間生成標(biāo)識(shí)符,并映射到塊文件名,然后放入一個(gè)名為chunk manifest的JS對(duì)象中。重點(diǎn)就在于!!當(dāng)我們使用CommonsChunkPlugin分離代碼時(shí),被分離出來的代碼(本文中的lodash庫,被打包為common。),會(huì)默認(rèn)被移動(dòng)到entry中最后一個(gè)入口進(jìn)行打包(第一個(gè)入口是index.js)。重要的是,chunk manifest將隨著這些被分離出來的代碼共同打包?。?!
由于我們更改源代碼后,不但會(huì)更新app的hash值,還會(huì)生成新的映射,然后新的映射又會(huì)和資源代碼一同打包,又由于chunkhash是根據(jù)內(nèi)容生成hash的,那么加入了新的映射對(duì)象chunk manifest的資源代碼被打包后,hash自然也會(huì)發(fā)生改變。這反過來,產(chǎn)生的新hash將使長(zhǎng)效緩存失效。
那么接下來我們需要做的就是講 manifest分離出來。這里我們利用一個(gè)CommonsChunkPlugin一個(gè)較少有人知道的功能,能夠在每次修改后的構(gòu)建中將manifest提取出來,通過指定entry中未用到的名稱,此插件會(huì)自動(dòng)將我們需要的內(nèi)容提取到單獨(dú)的包中。
故再額外配置一個(gè)CommonsChunkPlugin:
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
common: ['lodash'],
app: './src/index.js'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(['dist']),
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // 指代index.js引入的lodash庫
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest' // 用于提取manifest
})
]
}
webpack打包后:

從這里可以證明之前所說的manifest被打包進(jìn)了common?。?!仔細(xì)看之前的圖:common的Size都是547kb,到這里common大小是541kb 而manifest大小正好為5.85kb,加起來正好為547kb。
然后我們修改index.js再次打包:

從這里可以發(fā)現(xiàn)??!我們修改了源代碼,common的hash值已經(jīng)不再發(fā)生改變了!到這里可以達(dá)到我們不緩存源代碼緩存資源文件的目的了。
但是可別高興得太早??!我們做了一個(gè)很小的修改,交換了entry中 app 和 common的順序(對(duì)比上一個(gè)代碼段):
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js',
common: ['lodash']
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(['dist']),
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // 指代index.js引入的lodash庫
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest' // 用于提取manifest
})
]
}
打包后:

這里發(fā)現(xiàn)對(duì)比上一張圖片發(fā)現(xiàn),common的hash值又發(fā)生改變了??!而且根本沒有更改index.js的內(nèi)容app的hash也變了,只是換了一下順序而已!
大家注意看本張圖與上一張圖的模塊解析順序([1],[2],[3]...之后所對(duì)應(yīng)的模塊)。發(fā)現(xiàn)上一張圖,lodash第一個(gè)解析,而現(xiàn)在lodash最后一個(gè)解析。
這就是hash更變的原因:這是因?yàn)槊總€(gè)module.id 會(huì)基于默認(rèn)的解析順序(resolve order)進(jìn)行增量。也就是說,當(dāng)解析順序發(fā)生變化,ID 也會(huì)隨之改變,所以hash值也會(huì)發(fā)生變化。
有人可能會(huì)決定,一般我們都不會(huì)更換webpack.config.js中entry的入口順序,那么是否我就不會(huì)遇見這個(gè)問題了。答案是否定的,除否你能保證資源文件都寫在entry的頂部。否則會(huì)出現(xiàn)這樣的情況:
假如entry的順序?yàn)椋?app -> common, 那么解析順序?yàn)?index.js → lodash。 如果之后index.js引入了 print.js,那么解析順序變?yōu)?index.js → print.js -> lodash。
以上,我們并沒有在entry中更改入口順序,解析的順序還是會(huì)發(fā)生改變,common的hash還是會(huì)發(fā)生,不能緩存。
這里我們就引入一個(gè)新的組件:HashedModuleIdsPlugin:根據(jù)hash生成ID(NamedModulesPlugin也具有同樣的效果,但是是根據(jù)路徑名生成ID,可讀性更高,也由此編譯時(shí)間會(huì)相對(duì)長(zhǎng)一些)。 這樣module.id就不會(huì)使用數(shù)字標(biāo)識(shí)符,而使用hash:
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
common: ['lodash'],
app: './src/index.js'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(['dist']),
new webpack.HashedModuleIdsPlugin(), // 引入該插件
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // 指代index.js引入的lodash庫
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest' // 用于提取manifest
})
]
}
打包發(fā)現(xiàn),之前[ ]里都是數(shù)字,現(xiàn)在都是一些字符,

接下來,我們?cè)侔補(bǔ)pp和common的順序調(diào)換一下,并且隨意修改index.js,再次打包:

現(xiàn)在大功告成,common的hash沒有改變,而因?yàn)楦兞藘?nèi)容app的hash改變了,這正是我們想要的結(jié)果。
參考資料:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Javascript筆記一 js以及json基礎(chǔ)使用說明
JavaScript中的數(shù)據(jù)很簡(jiǎn)潔的。簡(jiǎn)單數(shù)據(jù)只有 undefined, null, boolean, number和string這五種,而復(fù)雜數(shù)據(jù)只有一種,即object。2010-05-05
微信 java 實(shí)現(xiàn)js-sdk 圖片上傳下載完整流程
這篇文章主要介紹了微信 java 實(shí)現(xiàn)js-sdk 圖片上傳下載完整流程的相關(guān)資料,需要的朋友可以參考下2016-10-10
JavaScript數(shù)據(jù)結(jié)構(gòu)之二叉樹的刪除算法示例
這篇文章主要介紹了JavaScript數(shù)據(jù)結(jié)構(gòu)之二叉樹的刪除算法,簡(jiǎn)單分析了javascript刪除數(shù)據(jù)結(jié)構(gòu)中二叉樹節(jié)點(diǎn)時(shí)所遇到的各種情況與相關(guān)的處理原理與算法實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-04-04
JS實(shí)現(xiàn)網(wǎng)頁每隔3秒彈出一次對(duì)話框的方法
這篇文章主要介紹了JS實(shí)現(xiàn)網(wǎng)頁每隔3秒彈出一次對(duì)話框的方法,涉及JavaScript結(jié)合時(shí)間函數(shù)遞歸調(diào)用的相關(guān)技巧,非常簡(jiǎn)單,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
淺談JavaScript Math和Number對(duì)象
這篇文章主要簡(jiǎn)單介紹了JavaScript Math和Number對(duì)象的相關(guān)資料,需要的朋友可以參考下2015-01-01
前端實(shí)現(xiàn)類似chatgpt的對(duì)話頁面(案例)
這篇文章主要介紹了前端實(shí)現(xiàn)類似chatgpt的對(duì)話頁面(案例),需要的朋友可以參考下2023-03-03
通過javascript進(jìn)行UTF-8編碼的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄ㄟ^javascript進(jìn)行UTF-8編碼的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06

