使用webpack構建工程的時候,咱們往常會把功能不一樣的代碼打包到不一樣的包裏(如lib,vendor,業務代碼)。 而持久緩存的目的就是每一次更新線上代碼的時候,儘量使內容未作更改的模塊的名字和以前保持一致。node
使用webpack實現持久緩存主要須要解決:webpack
webpack runtime代碼分離git
穩定moduleIDgithub
穩定chunkIDweb
基本配置 webpack有提供2種hash命名的方式,緩存
[hash]: 整次build生成一個惟一的hash值,賦給全部生成的文件。bash
顯然,爲了將文件名和內容相關聯,應該使用chunkhash。異步
output: {
path: path.join(__dirname, 'dist/js'),
filename: '[name]-[chunkhash:8].js',
chunkFilename: 'chunks/[name].[chunkhash:8].js'
},
複製代碼
這樣配置雖然能夠實現持久緩存,可是把全部的代碼都打到了一塊兒(vendor, 業務代碼等),顯示不符合要求。ide
使用CommonsChunkplugin將vendor單獨打包:學習
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function(module) {
return /node_modules/.test(module.context);
}
})
複製代碼
如今若是我對業務代碼進行更改:
const helloWorld = () => {
console.log(`moment : ${moment()}`);
};
複製代碼
按道理vendor已經單獨打包,改變業務代碼並不該該改變vendor的hash值,然而
緣由是CommonsChunkplugin把vendor單獨打出來的時候,還會將webpack本身生成的一部分runtime代碼一塊兒打進vendor. 以下圖,runtime裏牽扯到chunkid等容易頻繁變動的元素,因此當業務代碼發生變化的時候,runtime代碼也會變。
這個問題也容易解決,再用CommonsChunkplugin把runtime代碼單獨打出來(CommonsChunkplugin會把runtime的代碼打到配置指定的最後一個chunk裏):
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
})
複製代碼
然而還沒完,當咱們在業務代碼裏增長一個entry,vendor的hash值又發生了變化。
entry: {
main: "./src/index.tsx",
sub: "./src/sub.tsx",
},
複製代碼
形成這個問題的緣由是當咱們加入一個新entry的時候,由於webpack默認會依次用整數給這些module命名,若是新增的entry有新引入其餘module,就會打亂以前module的命名。好比說當只有一個entry的時候,業務代碼裏定義了module: 0,1,2,3。vendor裏定義了module 4,5。增長一個entry後:業務代碼裏會定義: 0,1,2,3,4。 vendor裏定義module 5,6. (其實,就算sub沒有引入新module,這裏若是把main和sub的位置換一下也會形成vendor hash值變化,這和chunkID的值有關,後面會介紹)
可見,要生成穩定的chunkhash值,首先必須解決moduleID的問題。
NamedModulesPlugin & HashedModuleIdsPlugin
NamedModulesPlugin:使用文件的相對路徑代替整數做爲moduleID.
var isNumber = __webpack_require__("./node_modules/array-first/node_modules/is-number/index.js");
var slice = __webpack_require__("./node_modules/array-slice/index.js");
複製代碼
不過也帶來2個問題:
1.用相對路徑代替數字,文件變大了
2.相對路徑暴露了
HashedModuleIdsPlugin :主要就是爲了解決以上2個問題,它對相對路徑進行一個md5的摘要,不只避免文件過大,也隱藏了路徑。
有時候一些模塊可能在頁面初始化的時候並用不到,可能會在以後的過程當中(好比用戶點擊事件)纔會用到,這一類模塊能夠經過動態import()來引入。
const helloWorld = () => {
import('./sub').then((module) => {
const sub = module.default;
sub();
})
};
複製代碼
能夠看到,動態引入的代碼會被單獨打包到chunks/裏的文件裏,並且vendor的值再次發生了改變。形成這個的緣由是和moduleID相似,webpack默認使用整數做爲chunkID,而且異步加載的chunk會先被賦值。 也就是說在沒有動態引入以前,vendor的chunkID是0,動態引入以後,vendor文件的chunkID變爲了1,因此形成了內容變化。(上面提到的改變entry順序也會形成chunkID的變化)
NamedChunksPlugin:這個插件會用chunk的字符串name代替整數做爲chunkID。不過要注意的是,動態生成的chunk並無名字,因此須要手動給取個名字。
new webpack.NamedChunksPlugin(function(chunk) {
if (chunk.name) {
return chunk.name;
}
return chunk.mapModules(function(m){ return m.id}).join("_");
})
複製代碼
至此,可能形成hash值不穩定的元素都被幹掉。
NamedModulesPlugin -> optimization.namedModules (on by default in develoment mode)
開發模式下默認會使用NamedModulesPlugin。目測咱們仍是須要本身解決chunkID的問題,具體怎麼操做等下個月出了穩定版再學習吧。