WebPack持久緩存學習小結

持久緩存

使用webpack構建工程的時候,咱們往常會把功能不一樣的代碼打包到不一樣的包裏(如lib,vendor,業務代碼)。 而持久緩存的目的就是每一次更新線上代碼的時候,儘量使內容未作更改的模塊的名字和以前保持一致。node

使用webpack實現持久緩存主要須要解決:webpack

  1. webpack runtime代碼分離git

  2. 穩定moduleIDgithub

  3. 穩定chunkIDweb

基本配置 webpack有提供2種hash命名的方式緩存

  1. [hash]: 整次build生成一個惟一的hash值,賦給全部生成的文件。bash

顯然,爲了將文件名和內容相關聯,應該使用chunkhash。異步

output: {
    path: path.join(__dirname, 'dist/js'),
    filename: '[name]-[chunkhash:8].js',
    chunkFilename: 'chunks/[name].[chunkhash:8].js'
  },
複製代碼

build結果
build結果

這樣配置雖然能夠實現持久緩存,可是把全部的代碼都打到了一塊兒(vendor, 業務代碼等),顯示不符合要求。ide

webpack-runtime

使用CommonsChunkplugin將vendor單獨打包:學習

new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: function(module) {
      return /node_modules/.test(module.context);
    }
})
複製代碼

打包結果
第三方庫被打到vendor裏

如今若是我對業務代碼進行更改:

const helloWorld = () => {
  console.log(`moment : ${moment()}`);
};
複製代碼

按道理vendor已經單獨打包,改變業務代碼並不該該改變vendor的hash值,然而

打包結果
vendor的hash值變了

緣由是CommonsChunkplugin把vendor單獨打出來的時候,還會將webpack本身生成的一部分runtime代碼一塊兒打進vendor. 以下圖,runtime裏牽扯到chunkid等容易頻繁變動的元素,因此當業務代碼發生變化的時候,runtime代碼也會變。

這個問題也容易解決,再用CommonsChunkplugin把runtime代碼單獨打出來(CommonsChunkplugin會把runtime的代碼打到配置指定的最後一個chunk裏):

new webpack.optimize.CommonsChunkPlugin({
    name: 'manifest',
    minChunks: Infinity
})
複製代碼

打包結果
runtime代碼被單獨提取到manifest裏
. 如今再更改業務代碼,業務代碼和manifest的內容會變,vendor文件的內容不會受影響。

Module ID

然而還沒完,當咱們在業務代碼裏增長一個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的摘要,不只避免文件過大,也隱藏了路徑。

Chunk ID

有時候一些模塊可能在頁面初始化的時候並用不到,可能會在以後的過程當中(好比用戶點擊事件)纔會用到,這一類模塊能夠經過動態import()來引入。

const helloWorld = () => {
 import('./sub').then((module) => {
     const sub = module.default;
     sub();
   })
};
複製代碼

'打包結果'
動態引入的chunk居然是用數字命名的

能夠看到,動態引入的代碼會被單獨打包到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("_");
})
複製代碼

打包結果
用該chunk所用到的全部module的ID來命名chunk

至此,可能形成hash值不穩定的元素都被幹掉。

Webpack4 更新

webpack4更新的內容

  1. CommonsChunksPlugin被幹掉了,解決runtime貌似只須要使用optimization.runtimeChunk就能搞定了
  2. NamedModulesPlugin -> optimization.namedModules (on by default in develoment mode) 開發模式下默認會使用NamedModulesPlugin。

目測咱們仍是須要本身解決chunkID的問題,具體怎麼操做等下個月出了穩定版再學習吧。

參考文獻

Predictable long term caching with Webpack

webpack文檔

用 webpack 實現持久化緩存

相關文章
相關標籤/搜索