在部署上線后經常出現js、css緩存問題 導致樣式等
區分不同的緩存
hash
hash是跟整個webpack構建項目相關的,每次項目構建hash對應的值都是不同的,即使項目文件沒有做“任何修改”。
其實是有修改的,因為每次webpack打包編譯都會注入webpack的運行時代碼,導致整個項目有變化,所以每次hash值都會變化的。
chunkhash
chunkhash,從字面上就能猜出它是跟webpack打包的chunk相關的。具體來說webpack是根據入口entry配置文件來分析其依賴項並由此來構建該entry的chunk,並生成對應的hash值。不同的chunk會有不同的hash值。一般在項目中把公共的依賴庫和程序入口文件隔離並進行單獨打包構建,用chunkhash來生成hash值,只要依賴公共庫不變,那么其對應的chunkhash就不會變,從而達到緩存的目的。
一般在項目中對webpack的entry使用chunkhash,具體表現在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' } ...
}
contenthash
contenthash表示由文件內容產生的hash值,內容不同產生的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文件所處的模塊里就算其他文件內容改變,只要css文件內容不變,它的hash值就不會變。
實現js緩存
webpack插件CommonsChunkPlugin
的主要作用是抽取webpack項目入口chunk的公共部分,具體的用法就不做過多介紹,不太了解可以參考webpack官網介紹;
該插件是webpack項目常用的一個優化功能,幾乎在每個webpack項目中都會用到。使用該插件帶來的好處:
-
提升webpack打包速度和項目體積:將webpack入口的chunk文件中所有公共的代碼提取出來,減少代碼體積;同時提升webpack打包速度。
-
利用緩存機制:依賴的公共模塊文件一般很少更改或者不會更改,這樣獨立模塊文件提取出可以長期緩存。
但是在項目中,若插件打開方式不正確的話,上面的第二點其實是無法實現,因為這種情況下:
沒有被修改過的公有代碼或庫代碼打包出的Entry Chunk,會隨着其他業務代碼的變化而變化,導致頁面上的長緩存機制失效。
那么,下面就來開啟CommonsChunkPlugin
正確的打開方式。
CommonsChunkPlugin不正確用法
假如將我們項目的公共庫如react、react-dom、react-router與業務代碼隔離,將其提取為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"]}) ] };
引起問題的原因
引起webpack每次打包編譯時vendor跟着變化的原因:
webpack每次build的時候都會生成一些運行時代碼。當只有一個文件時,運行時代碼直接塞到這個文件中。當有多個文件時,運行時代碼會被提取到公共文件中,也就是上面CommonsChunkPlugin配置的vendor chunk中。
webpack每次編譯時產生的運行時代碼,包括全局webpackJsonp方法的定義和維護模塊依賴關系,具體可以參考這里>>。
所以,上面webpack的CommonsChunkPlugin配置中,每次編譯時這些代碼都會打包到vendor中,導致每次vendor的chunkhash每次都會變化。
在webpack中配置CommonsChunkPlugin時需要注意幾點:
1、 配置webpack的output項時,其
filename
和chunkFilename
必須使用chunkhash。不要使用hash,否則即使按照上面的配置也不能達到預期的效果。至於hash與chunkhash的區別
2、對於圖片、字體等靜態資源抽離使用的
file-loader
,其配置的hash表示的是靜態文件的內容hash值,不是webpack每次打包編譯生成的hash值, 切記!!!
3、對於抽取的css樣式文件,需要使用
contenthash
, 與file-loader
中的hash意義相同。此處不能為chunkhash,否則其與抽取該樣式文件的entry chunk的chunkhash保持一致,打不到緩存的目的。
實現圖片/字體的緩存
對於圖片、字體等靜態資源,在使用webpack構建提取時,其實是使用了file-loader
來完成的,生成對應的文件hash值也就是由對應的file-loader
來計算的。那么這些靜態文件的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每次項目構建的hash,它是由file-loader根據文件內容計算出來的,不要誤認為是webpack構建的hash。
解決的方案有三種:
1、用戶自行清理瀏覽器緩存。
2、使用禁用緩存標簽,實現禁用瀏覽器緩存。
3、為js和css文件添加版本號。
1.一般沒這么干的
2.
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Cache" content="no-cache">
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
3.使用webpack構建的時候css、js添加hash進行改變