webpack進階——緩存與獨立打包

系列:
webpack入門——了解及使用
webpack基礎——常用配置解析

先來看看最基礎的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());

打包之后鳄炉,只會生成一個bundle.js丑婿,這樣的話,每次若要加載資源文件,瀏覽器都會加載根本不會改動的lodash庫诉位,這樣很低效。
由于如果每次去訪問瀏覽器菜枷,瀏覽器都重新下載資源苍糠,由于網(wǎng)絡獲取資源可能很慢,可能頁面久久加載不出來啤誊,低效且不友好岳瞭,故瀏覽器會緩存資源,以避免每次訪問都通過網(wǎng)絡去獲取資源蚊锹。
但是瞳筏,由于瀏覽器緩存,又會出現(xiàn)新的問題牡昆,如果我們部署版本時不更改資源的文件名姚炕,瀏覽器可能認為它沒有更新,就會使用它的緩存版本。
這樣我們就需要解決兩個問題:第一柱宦,分離打包文件些椒。第二,解決緩存問題掸刊。

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庫
    })
  ]
}

主要變動:

  • 添加插件:CommonsChunkPlugin免糕,提取引入的庫,并且更名忧侧,實現(xiàn)代碼分離石窑。
  • 輸出上在名字上加了hash,每次打包后蚓炬,hash值都不一樣解決了瀏覽器緩存的問題尼斧。

結果:index.js打包為app.[hash].js,index.js引入的lodash打包為common.[hash].js试吁。這樣解決了瀏覽器緩存問題和實現(xiàn)了靜態(tài)資源代碼和源代碼的分離棺棵,但是新的問題又出現(xiàn)了。

第一次打包后(注意Asset列下的名字):


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


而且common.[hash].js的hash值與app的hash值相同也發(fā)生了變化(可以自行測試一下余耽,先webpack打包一次缚柏,修改index.js后再次打包一次)。

這并不是我們想要的結果碟贾,雖然源代碼hash改變解決了瀏覽器使用緩存版本的問題币喧,但是,如果common.js的hash值也一同發(fā)生了變化的話袱耽,那么瀏覽器也還需要每次都請求不會發(fā)生改變的靜態(tài)代碼common杀餐,這樣還是浪費了網(wǎng)絡資源,很低效朱巨。

注:本案例會多次打包史翘,dist目錄中會生成過多垃圾文件,在實際使用中都使用了CleanWebpackPlugin插件冀续。
new CleanWebpackPlugin(['dist']) // 加入在插件數(shù)組中琼讽,用于在每次打包前,都清空打包文件夾下之前打包的文件洪唐。

如果修改了index钻蹬,僅僅只是生成的app的hash值發(fā)生變化,而common的hash值不發(fā)生變化凭需,那就能夠達到我們的目的问欠,既能緩存庫又能識別源文件的更改肝匆。
我們進行如下配置: output中將 [name].[hash].js 改為[name].[chunkhash].js ,讓每個文件生成唯一的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]溅潜,因為這會增加編譯時間术唬。將開發(fā)和生產(chǎn)模式的配置分開,并在開發(fā)模式中使用 [name].js 的文件名滚澜,在生產(chǎn)模式中使用 [name].[chunkhash].js 文件名粗仓,所以如果這個時候使用了熱替換插HotModuleReplacementPlugin,將會導致編譯不成功I杈琛)
我們配置好之后借浊,進行webpack打包:


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


奇怪的是,雖然common與app生成了單獨的hash值槐沼,但是修改了index.js曙蒸,common的hash值還是發(fā)生了變化。

原因是:為了最小化生成的文件大小岗钩,webpack使用標識符而不是模塊名稱纽窟,在編譯期間生成標識符,并映射到塊文件名兼吓,然后放入一個
名為chunk manifest的JS對象中臂港。重點就在于!视搏!當我們使用CommonsChunkPlugin分離代碼時审孽,被分離出來的代碼(本文中的lodash庫,
被打包為common浑娜。)佑力,會默認被移動到entry中最后一個入口進行打包(第一個入口是index.js)。重要的是棚愤,chunk manifest將隨著這些被分離出來的代碼共同打包4晗簟!宛畦!
由于我們更改源代碼后,不但會更新app的hash值揍移,還會生成新的映射次和,然后新的映射又會和資源代碼一同打包,又由于chunkhash是根據(jù)內(nèi)容生成hash的那伐,那么加入了新的映射對象chunk manifest的資源代碼被打包后踏施,hash自然也會發(fā)生改變石蔗。這反過來,產(chǎn)生的新hash將使長效緩存失效畅形。

那么接下來我們需要做的就是講 manifest分離出來养距。這里我們利用一個CommonsChunkPlugin一個較少有人知道的功能,能夠在每次修改后的構建中將manifest提取出來日熬,通過指定entry中未用到的名稱棍厌,此插件會自動將我們需要的內(nèi)容提取到單獨的包中。

故再額外配置一個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被打包進了commonJT派础!仔細看之前的圖:common的Size都是547kb毕荐,到這里common大小是541kb 而manifest大小正好為5.85kb束析,加起來正好為547kb。
然后我們修改index.js再次打包:


從這里可以發(fā)現(xiàn)T餮恰员寇!我們修改了源代碼,common的hash值已經(jīng)不再發(fā)生改變了第美!到這里可以達到我們不緩存源代碼緩存資源文件的目的了蝶锋。
但是可別高興得太早!斋日!我們做了一個很小的修改牲览,交換了entry中 app 和 common的順序(對比上一個代碼段):

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)對比上一張圖片發(fā)現(xiàn),common的hash值又發(fā)生改變了6袷亍第献!而且根本沒有更改index.js的內(nèi)容app的hash也變了,只是換了一下順序而已兔港!
大家注意看本張圖與上一張圖的模塊解析順序([1]庸毫,[2],[3]...之后所對應的模塊)衫樊。發(fā)現(xiàn)上一張圖飒赃,lodash第一個解析,而現(xiàn)在lodash最后一個解析科侈。
這就是hash更變的原因:這是因為每個 module.id 會基于默認的解析順序(resolve order)進行增量载佳。也就是說,當解析順序發(fā)生變化臀栈,ID 也會隨之改變蔫慧,所以hash值也會發(fā)生變化。
有人可能會決定权薯,一般我們都不會更換webpack.config.js中entry的入口順序姑躲,那么是否我就不會遇見這個問題了睡扬。答案是否定的,除否你能保證資源文件都寫在entry的頂部黍析。否則會出現(xiàn)這樣的情況:
假如entry的順序為: app -> common卖怜, 那么解析順序為 index.js → lodash。 如果之后index.js引入了 print.js阐枣,那么解析順序變?yōu)?index.js → print.js -> lodash马靠。
以上,我們并沒有在entry中更改入口順序侮繁,解析的順序還是會發(fā)生改變虑粥,common的hash還是會發(fā)生,不能緩存宪哩。
這里我們就引入一個新的組件:HashedModuleIdsPlugin:根據(jù)hash生成ID(NamedModulesPlugin也具有同樣的效果娩贷,但是是根據(jù)路徑名生成ID,可讀性更高锁孟,也由此編譯時間會相對長一些)彬祖。 這樣module.id就不會使用數(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)在都是一些字符,



接下來圆恤,我們再把app和common的順序調(diào)換一下突倍,并且隨意修改index.js,再次打包:



現(xiàn)在大功告成盆昙,common的hash沒有改變羽历,而因為更變了內(nèi)容app的hash改變了,這正是我們想要的結果淡喜。

參考資料:
webpack文檔 -- 緩存
webpack獨立打包與緩存處理

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秕磷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子炼团,更是在濱河造成了極大的恐慌澎嚣,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘟芝,死亡現(xiàn)場離奇詭異易桃,居然都是意外死亡,警方通過查閱死者的電腦和手機锌俱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門颈抚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嚼鹉,你說我怎么就攤上這事贩汉。” “怎么了锚赤?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵匹舞,是天一觀的道長。 經(jīng)常有香客問我线脚,道長赐稽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任浑侥,我火速辦了婚禮姊舵,結果婚禮上,老公的妹妹穿的比我還像新娘寓落。我一直安慰自己括丁,他們只是感情好,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布伶选。 她就那樣靜靜地躺著史飞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仰税。 梳的紋絲不亂的頭發(fā)上构资,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機與錄音陨簇,去河邊找鬼吐绵。 笑死,一個胖子當著我的面吹牛河绽,可吹牛的內(nèi)容都是我干的己单。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼葵姥,長吁一口氣:“原來是場噩夢啊……” “哼荷鼠!你這毒婦竟也來了?” 一聲冷哼從身側響起榔幸,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤允乐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后削咆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牍疏,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年拨齐,在試婚紗的時候發(fā)現(xiàn)自己被綠了鳞陨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖厦滤,靈堂內(nèi)的尸體忽然破棺而出援岩,到底是詐尸還是另有隱情,我是刑警寧澤掏导,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布享怀,位于F島的核電站,受9級特大地震影響趟咆,放射性物質發(fā)生泄漏添瓷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一值纱、第九天 我趴在偏房一處隱蔽的房頂上張望鳞贷。 院中可真熱鬧,春花似錦虐唠、人聲如沸搀愧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽查近。三九已至俱饿,卻和暖如春冤寿,著一層夾襖步出監(jiān)牢的瞬間育苟,已是汗流浹背慢逾。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工歧强, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留反番,地道東北人沙热。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像罢缸,于是被迫代替她去往敵國和親篙贸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容