想把node_modules/.cache目錄上傳到一箇中心化倉庫,而後在CI過程構建的時候,能夠拉下來重複利用。可是實踐中,雖然.cache的確拉下來了,業務代碼也一點沒變,可是緩存就是沒有生效。探究其緣由,最後發現是cache-loader的基於時間(mtime)的緩存驗證機制帶來的問題。vue
cache-loader做爲webpack的loader,會在pitch和loader兩個階段分別作一些事情:node
pitch階段:校驗緩存文件是否可用webpack
loader階段:判斷當前loader的文件是否須要從新生成緩存git
pitch和loader與DOM的capture和pop很像,假設有這樣一個loader配置:github
loader: ['cache-loader', 'vue-loader']複製代碼
那麼pitch階段的處理流程是:cache-loader -> vue-loader,而loader階段的處理流程是:vue-loader -> cache-loader。而且,在這兩個階段能夠經過一個共享的data對象來傳遞消息web
根據當前正在處理的文件,讀取.cache目錄中對應的cache文件,這個文件主要主要有兩部份內容:緩存
當前正在處理的文件所依賴的文件bash
當前正在處理的文件,在上一次loader過程當中的產物spa
其中第1點用來判斷當前文件的緩存是否依然有效,若是判斷有效,那麼就直接複用第2點的內容。code
在這個階段,咱們要判斷當前文件是否須要從新生成緩存,判斷邏輯很簡單:
若是pitch階段的判斷當前文件的緩存失效了,那麼loader階段就要去生成緩存。
在CI環境中,屢次部署之間是隔離的,也就是說node_modules中的全部文件每次都會從新生成,因此node_modules/.cache目錄天然就丟失了。
那咱們天然會想到一個辦法:在編譯後把node_modules/.cache存到雲上,而後在下次編譯的時候再來下來。的確,這個方案是沒問題的,
可是在實施的過程當中,卻遇到了一個問題:上述先存再拉的流程確實執行了,可是cache-loader仍是會從新生成緩存,並無利用上。
基於這個現象去看了cache-loader代碼後發現,在cache-loader的pitch階段,它的「判斷當前文件的緩存是否依然有效」的方法是:基於文件最後修改時間(mtime)來判斷。
簡單的來講就是:咱們雖然從雲上拉下來了上一次編譯產生的緩存文件,與yarn從新安裝的node_modules裏的文件,存在mtime不一致問題,致使判斷爲「緩存失效」。
找到問題的緣由後,翻了翻cache-loader的issues,發現你們也在吐槽只支持mtime判斷這件事,可是cache-loader不打算推出新的判斷方式,而是推薦你們等webpack5的多級緩存功能。
基於這個事實,咱們只能去二次開發cache-loader,讓它支持新的對比方式,從而解決上述問題。
cache-loader提供了自定義compare方法的接口,可是這個接口回調的參數沒法讓咱們去獲取文件hash,因此至關因而個擺設。
同時這個compare接口也已經上線了,再去改compare接口的回調參數,是一個breaking change,也不太現實。
咱們天然想到判斷文件是否發生變更的方法:hash對比。由於hash不會隨時間地點變化,它能夠完美解決上述問題。具體方案是:
在pitch階段使用hash對比判斷緩存是否有效,若是緩存無效或沒有緩存,接着在loader階段讀取文件並生成hash存入到緩存中。