webpack實現(xiàn)靜態(tài)資源緩存的方法
引言
靜態(tài)資源緩存是前端性能優(yōu)化的一個點,所以在前端開發(fā)過程中,一般會最大限度的利用緩存(這里主要是強緩存)?;氐奖疚闹黝},在使用webpack構(gòu)建的項目中,稍有不慎的話,即使服務器設置了緩存策略,可能構(gòu)建的項目無法實現(xiàn)靜態(tài)資源緩存。那么webpack怎樣才能達到使用緩存的效果呢,下面就來談談這個問題。
區(qū)分一下幾種不同的hash
我們都知道,webpack有各種hash值,包括每次項目構(gòu)建hash
,不同入口的chunkhash
、文件的內(nèi)容contenthash
,這么多hash,它們有什么區(qū)別呢?
hash
hash是跟整個webpack構(gòu)建項目相關的,每次項目構(gòu)建hash對應的值都是不同的,即使項目文件沒有做“任何修改”。
其實是有修改的,因為每次webpack打包編譯都會注入webpack的運行時代碼,導致整個項目有變化,所以每次hash值都會變化的。
以本人項目代碼為例,代碼兩次構(gòu)建前后沒有做任何修改的對比圖
可以看出,前后兩次對應項目構(gòu)建hash改變了。由此推斷使用該方式是無法達到緩存的,因為每次hash都會變化。
chunkhash
chunkhash,從字面上就能猜出它是跟webpack打包的chunk相關的。具體來說webpack是根據(jù)入口entry配置文件來分析其依賴項并由此來構(gòu)建該entry的chunk,并生成對應的hash值。不同的chunk會有不同的hash值。一般在項目中把公共的依賴庫和程序入口文件隔離并進行單獨打包構(gòu)建,用chunkhash來生成hash值,只要依賴公共庫不變,那么其對應的chunkhash就不會變,從而達到緩存的目的。
一般在項目中對webpack的entry使用chunkhash,具體表現(xiàn)在output配置項上:
moudule.exports = { entry: { app: './src/main.js', vendor: ['react', 'redux', 'react-dom', 'react-redux', 'react-router-redux'] }, output: { path:path.join(__dirname, '/dist/js'), filename: '[name].[chunkhash].js' } ... }
最后app和vendor的chunkhash編譯結(jié)果如下圖
contenthash
contenthash表示由文件內(nèi)容產(chǎn)生的hash值,內(nèi)容不同產(chǎn)生的contenthash值也不一樣。在項目中,通常做法是把項目中css都抽離出對應的css文件來加以引用。比方在webpack配置這樣來用:
module.exports = { ... plugins: [ new ExtractTextPlugin({ filename: 'static/[name]_[chunkhash:7].css', disable: false, allChunks: true }) ... ]
上面配置有一個問題,因為使用了chunkhash,它與依賴它的chunk共用chunkhash。
比方在上面app chunk例子中依賴一個index.css文件,index.css的hash是跟著app的chunkhash走的,只要app文件變更的話,那么即使index.css文件沒有變化,它的hash值也是會跟著變化的,導致緩存失效。
那么這時我們可以使用extra-text-webpack-plugin里的contenthash
值,保證即使css文件所處的模塊里就算其他文件內(nèi)容改變,只要css文件內(nèi)容不變,它的hash值就不會變。
實現(xiàn)js緩存
webpack插件CommonsChunkPlugin
的主要作用是抽取webpack項目入口chunk的公共部分,具體的用法就不做過多介紹,不太了解可以參考webpack官網(wǎng)介紹;
該插件是webpack項目常用的一個優(yōu)化功能,幾乎在每個webpack項目中都會用到。使用該插件帶來的好處:
- 提升webpack打包速度和項目體積:將webpack入口的chunk文件中所有公共的代碼提取出來,減少代碼體積;同時提升webpack打包速度。
- 利用緩存機制:依賴的公共模塊文件一般很少更改或者不會更改,這樣獨立模塊文件提取出可以長期緩存。
但是在項目中,若插件打開方式不正確的話,上面的第二點其實是無法實現(xiàn),因為這種情況下:
沒有被修改過的公有代碼或庫代碼打包出的Entry Chunk,會隨著其他業(yè)務代碼的變化而變化,導致頁面上的長緩存機制失效。
那么,下面就來開啟CommonsChunkPlugin
正確的打開方式。
CommonsChunkPlugin不正確用法
假如將我們項目的公共庫如react、react-dom、react-router與業(yè)務代碼隔離,將其提取為vendor chunk,webpack配置如下:
const webpack = require("webpack"); const path = require('path'); module.exports = { entry: { app: "./src/main.js", vendor: ["react","react-dom", "redux", "react-redux", "react-router-redux"] }, output: { path: path.resolve(__dirname, 'output'), filename: "[name].[chunkhash].js" }, plugins: [ new webpack.optimize.CommonsChunkPlugin({names: ["vendor"]}) ] };
上面將項目一些基礎庫打包成一個名為vendor的chunk中,并將業(yè)務相關的代碼打包到一個名為app的chunk中;
webpack打包編譯后的結(jié)果如下:
我們對其中的業(yè)務代碼app.js進行修改后,重新編譯結(jié)果如下:
可以發(fā)現(xiàn),在CommonsChunkPlugin這種配置下,當業(yè)務代碼app發(fā)生變化,而庫代碼也跟著變化,vender的chunkhash也跟著變化,這樣vendor的引用的名稱跟著變化,導致瀏覽器端的長緩存機制失效。
引起問題的原因
引起webpack每次打包編譯時vendor跟著變化的原因:
webpack每次build的時候都會生成一些運行時代碼。當只有一個文件時,運行時代碼直接塞到這個文件中。當有多個文件時,運行時代碼會被提取到公共文件中,也就是上面CommonsChunkPlugin配置的vendor chunk中。
webpack每次編譯時產(chǎn)生的運行時代碼,包括全局webpackJsonp方法的定義和維護模塊依賴關系,具體可以參考這里>>。
所以,上面webpack的CommonsChunkPlugin配置中,每次編譯時這些代碼都會打包到vendor中,導致每次vendor的chunkhash每次都會變化。
那么,我們可以在對vendor chunk進行配置,抽取其中的公共代碼,即webpack運行時代碼,這樣就可以將項目依賴的基礎庫模塊與業(yè)務模塊隔離開來,因為不會對這些文件進行修改,所以這些文件可達到長緩存的作用。具體配置如下:
module.exports = { entry: { app: "./app.js", vendor: ["react","react-dom", "redux", "react-redux", "react-router-redux"] }, .... plugins: [ new webpack.optimize.CommonsChunkPlugin({names: ["vendor"]}), new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', chunks: ['vendor'] }) ] };
這樣,即使修改業(yè)務app代碼,項目依賴的基礎庫vendor chunk也不會發(fā)生變化;只是抽取的manifest chunk每次還會變化,但是這個文件體積非常小,相比vendor來說這種方式的收益更大。如下圖:
修改app代碼后的打包編譯結(jié)果如下,可以看到vendor的chunkhash沒有變化
在webpack中配置CommonsChunkPlugin時需要注意幾點:
1、 配置webpack的output項時,其filename
和chunkFilename
必須使用chunkhash。不要使用hash,否則即使按照上面的配置也不能達到預期的效果。至于hash與chunkhash的區(qū)別,可參考github的回答
2、對于圖片、字體等靜態(tài)資源抽離使用的file-loader
,其配置的hash表示的是靜態(tài)文件的內(nèi)容hash值,不是webpack每次打包編譯生成的hash值, 切記?。?!
3、對于抽取的css樣式文件,需要使用contenthash
, 與file-loader
中的hash意義相同。此處不能為chunkhash,否則其與抽取該樣式文件的entry chunk的chunkhash保持一致,打不到緩存的目的。
實現(xiàn)css的緩存
webpack實現(xiàn)css的緩存,就是使用上面介紹過的contenthash,該hash屬性值其實是extra-text-webpack-plugin
計算的。具體實現(xiàn)css的緩存,其實就像下面一樣使用contenthash即可
module.exports = { ... plugins: [ new ExtractTextPlugin({ filename: 'static/[name]_[contenthash:7].css', disable: false, allChunks: true }) ... ]
實現(xiàn)圖片/字體的緩存
對于圖片、字體等靜態(tài)資源,在使用webpack構(gòu)建提取時,其實是使用了file-loader
來完成的,生成對應的文件hash值也就是由對應的file-loader
來計算的。那么這些靜態(tài)文件的hash值使用的是什么hash值呢,其實就是hash
屬性值。如下面代碼所示:
module.exports = { ... rules: [ ... { test: /\.(gif|png|jpe?g)(\?\S*)?$/, loader: require.resolve('url-loader'), options: { limit: 10000, name: path.posix.join('static', '[name]_[hash:7].[ext]') } }, font: { test: /\.otf|ttf|woff2?|eot(\?\S*)?$/, loader: require.resolve('url-loader'), options: { limit: 10000, name: path.posix.join('static', '[name]_[hash:7].[ext]') } } ] }
可以看到上面使用的是hash屬性值,此hash非webpack每次項目構(gòu)建的hash,它是由file-loader根據(jù)文件內(nèi)容計算出來的,不要誤認為是webpack構(gòu)建的hash。
參考
1、webpack之CommonsChunkPlugin正確打開方式
2、webpack 填坑之路--提取獨立文件(模塊)
3、webpack代碼分割技巧
4、聽說你用webpack處理文件名的hash?那么建議你看看你生成的hash對不對
5、chunkhash
6、multiple-commons-chunks
7、用 webpack 實現(xiàn)持久化緩存
8、Webpack中hash與chunkhash的區(qū)別,以及js與css的hash指紋解耦方案
到此這篇關于webpack實現(xiàn)靜態(tài)資源緩存的方法的文章就介紹到這了,更多相關webpack 靜態(tài)資源緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Webpack常見靜態(tài)資源處理-模塊加載器(Loaders)+ExtractTextPlugin插件
- 詳解vue-cli與webpack結(jié)合如何處理靜態(tài)資源
- vue-cli與webpack處理靜態(tài)資源的方法及webpack打包的坑
- vue填坑之webpack run build 靜態(tài)資源找不到的解決方法
- 淺談webpack4 圖片處理匯總
- webpack 靜態(tài)資源集中輸出的方法示例
- 淺談 Webpack 如何處理圖片(開發(fā)、打包、優(yōu)化)
- 在Webpack中用url-loader處理圖片和字體的問題
- vue項目之webpack打包靜態(tài)資源路徑不準確的問題
- webpack5處理圖片、圖標字體、多媒體等靜態(tài)資源文件
相關文章
js 右側(cè)浮動層效果實現(xiàn)代碼(跟隨滾動)
因為項目上有這樣的需求,在網(wǎng)上也查了些東西,之前是想找個差不多類似的套用一下。后來發(fā)覺沒有合適的,因時間緊迫就自己動手寫了一個簡單的 ,示例代碼如下 兼容火狐和IE7+2015-11-11js文件中調(diào)用js的實現(xiàn)方法小結(jié)
JavaScript文件引用JavaScript文件的方法,需要的朋友可以參考下。2009-10-10重載toString實現(xiàn)JS HashMap分析
用過Java的都知道,里面有個功能強大的數(shù)據(jù)結(jié)構(gòu)——HashMap,它能提供鍵與值的對應訪問。不過熟悉JS的朋友也會說,JS里面到處都是hashmap,因為每個對象都提供了map[key]的訪問形式。2011-03-03利用Print.js實現(xiàn)打印pdf、HTML及圖片(可設置樣式可分頁)
在我們需要在頁面中打印某個區(qū)域的內(nèi)容或者生成pdf的時候,我們可以直接用printJs庫,這篇文章主要給大家介紹了關于利用Print.js實現(xiàn)打印pdf、HTML及圖片的相關資料,可設置樣式可分頁,需要的朋友可以參考下2024-05-05JavaScript編寫Chrome擴展實現(xiàn)與瀏覽器的交互及時間通知
得益于API,我們可以用JavaScript編寫Chrome擴展實現(xiàn)與瀏覽器的交互及時間通知。值得一提的是現(xiàn)在Chrome擁有后臺進程可以使通知在前臺瀏覽器關閉的情況下依然能夠生效.2016-05-05