国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

webpack多頁應用架構系列(十六):善用瀏覽器緩存,該去則去,該留則留

pekonchan / 3167人閱讀

摘要:瀏覽器緩存簡單介紹下面來簡單介紹一下瀏覽器緩存,以及為何我要在標題中強調該去則去,該留則留。但后來我還是反轉了自己,這種方法雖然能留下瀏覽器緩存,卻做不到該去則去。

本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。
原文地址:https://segmentfault.com/a/1190000010317802
如果您對本系列文章感興趣,歡迎關注訂閱這里:https://segmentfault.com/blog/array_huang
前言

一個成熟的項目,自然離不開迭代更新;那么在部署前端這一塊,我們免不了總是要顧及到瀏覽器緩存的,本文將介紹如何在 webpack (架構)的幫助下,妥善處理好瀏覽器緩存。

實際上,我很早以前就想寫這一part了,只是苦于當時我所掌握的方案不如人意,便不敢獻丑了;而自從
webpack 升級到 v2 版本后,以及第三方plugin的日益豐富,我們也有了更多的手段來處理cache。

瀏覽器緩存簡單介紹

下面來簡單介紹一下瀏覽器緩存,以及為何我要在標題中強調“該去則去,該留則留”。

瀏覽器緩存是啥?

瀏覽器緩存(Browser Cache),是瀏覽器為了節省網絡帶寬、加快網站訪問速度而推出的一項功能。瀏覽器緩存的運行機制是這樣的:

用戶使用瀏覽器第一次訪問某網站頁面,該頁面上引入了各種各樣的靜態資源(js/css/圖片/字體……),瀏覽器會把這些靜態資源,甚至是頁面本身(html文件),都一一儲存到本地。

用戶在后續的訪問中,如果需要再次請求同樣的靜態資源(根據 url 進行匹配),且靜態資源沒有過期(服務器端有一系列判別資源是否過期的策略,比如Cache-ControlPragmaETagExpiresLast-Modified),則直接使用前面本地儲存的資源,而不需要重復請求。

由于webpack只負責構建生成網站前端的靜態資源,不涉及服務器,因此本文不討論以HTTP Header為基礎的緩存控制策略;那我們討論什么呢?

很簡單,由于瀏覽器是根據靜態資源的url來判斷該靜態資源是否已有緩存,而靜態資源的文件目錄又是相對固定的,那么重點明顯就在于靜態資源的文件名了;我們就通過操控靜態資源的文件名,來決定靜態資源的“去留”。

瀏覽器緩存,該留不留會怎么樣?

每次部署上線新版本,靜態資源的文件名若有變化,則瀏覽器判斷是第一次讀取這個靜態資源;那么,即便這個靜態資源的內容跟上一版的完全一致,瀏覽器也要重新下載這個靜態資源,浪費網絡帶寬、拖慢頁面加載速度。

瀏覽器緩存,該去不去會怎么樣?

每次部署上線新版本,靜態資源的文件名若沒有變化,則瀏覽器判斷可加載之前緩存下來的靜態資源;那么,即便這個靜態資源的內容跟上一版的有所變化,瀏覽器也察覺不到,使用了老版本的靜態資源。那這會造成什么樣的影響呢?可大可小,小至用戶看到的依然是老版的資源,達不到上線更新版本的目的;大至造成網站運行報錯、布局錯位等問題。

如何通過操控靜態資源的文件名達到控制瀏覽器緩存的目的呢?

在webpack關于文件名命名的配置中,存在一系列的變量(或者理解成命名規則也可),通過這些變量,我們可以根據所要生成的文件的具體情況來進行命名,而不必預設好一個固定的名稱。在緩存處理這一塊,我們主要用到[hash][chunkhash]這兩個變量。關于這兩個變量的介紹,我在之前的文章 —— 《webpack配置常用部分有哪些?》就已經解釋過是什么意思了,這里就不再累述。

這里總結下[hash][chunkhash]這兩個變量的用法:

[hash]的話,由于每次使用 webpack 構建代碼的時候,此 hash 字符串都會更新,因此相當于強制刷新瀏覽器緩存

[chunkhash]的話,則會根據具體 chunk 的內容來形成一個 hash 字符串來插入到文件名上;換句說, chunk 的內容不變,該 chunk 所對應生成出來的文件的文件名也不會變,由此,瀏覽器緩存便能得以繼續利用

有哪些資源是需要兼顧瀏覽器緩存的?

理論上來說,除了HTML文件外(HTML文件的路徑需要保持相對固定,只能從服務器端入手),webpack生成的所有文件都需要處理好瀏覽器緩存的問題。

js

在 webpack 架構下,js文件也有不同類型,因此也需要不同的配置:

入口文件(Entry):在webpack配置中的output.filename參數中,讓生成的文件名中帶上[chunkhash]即可。

異步加載的chunk:output.chunkFilename參數,操作同上。

通過CommonsChunkPlugin生成的文件:在CommonsChunkPlugin的配置參數中有filename這一項,操作同上。但需要注意的是,如果你使用[chunkhash]的話,webpack 構建的時候可是會報錯的哦;那可咋辦呢,用[hash]的話,這common chunk不就每次上線新版本都強制刷新了嗎?這其實是因為,webpack 的 runtime && manifest 會統一保存在你的common chunk里,解決的方法,就請看下面關于“webpack 的 runtime && manifest”的部分了。

css

對于css來說,如果你是用style-loader直接把css內聯到里的,那么,你管好引入該css的js文件的瀏覽器緩存就好了。

而如果你是使用extract-text-webpack-plugin把css獨立打包成css文件的,那么在文件名的配置上,同樣加上[chunkhash]即可加上[contenthash]即可(感謝@FLYiNg_hbt 提醒)。這個[contenthash]是什么東西呢?其實就是extract-text-webpack-plugin為了與[chunkhash]區分開,而自定義的一個命名規則,其實際含義跟[chunkhash]可以說是一致的,只是[chunkhash]已被占用作為 chunk 的內容 hash 字符串了,繼續用[chunkhash]會造成下述問題。

圖片、字體文件等靜態資源

如《聽說webpack連圖片和字體也能打包?》里介紹的,處理這類靜態資源一般使用url-loaderfile-loader

對于url-loader來說,就不需要關心瀏覽器緩存了,因為它是把靜態資源轉化成 dataurl 了,而并非獨立的文件。

而對于file-loader來說,同樣是在文件名的配置上加上[chunkhash]即可。另外需要注意的是,url-loader一般搭配有降級到file-loader的配置(使用loader加載的文件大于一個你設定的值就降級到使用file-loader來加載),同樣需要在文件名的配置上加上[chunkhash]

webpack 的runtime && manifest

所謂的runtime,就是幫助 webpack 編譯構建后的打包文件在瀏覽器運行的一些輔助代碼段,換句話說,打包后的文件,除了你自己的源碼和npm庫外,還有 webpack 提供的一點輔助代碼段。

而 manifest,則是 webpack 用以查找 chunk 真實路徑所使用的一份關系表,簡單來說,就是 chunk 名對應 chunk 路徑的關系表。manifest 一般來說會被藏到 runtime 里,因此我們查看 runtime 的時候,雖然能找得到 manifest,但一般都不那么直觀,形如下面這一段(僅common chunk部分):

u.type = "text/javascript", u.charset = "utf-8", u.async = !0, u.timeout = 12e4, n.nc && u.setAttribute("nonce", n.nc), u.src = n.p + "" + e + "." + {
    0: "e6d1dff43f64d01297d3",
    1: "7ad996b8cbd7556a3e56",
    2: "c55991cf244b3d833c32",
    3: "ecbcdaa771c68c97ac38",
    4: "6565e12e7bad74df24c3",
    5: "9f2774b4601839780fc6"
}[e] + ".bundle.js";
runtime && manifest被打包到哪里去了?

那么,這runtime && manifest的代碼段,會被放到哪里呢?一般來說,如果沒有使用CommonsChunkPlugin生成common chunkruntime && manifest會被放在以入口文件為首的chunk(俗稱“大包”)里,如果是我們這種多頁(又稱多入口)應用,則會每個大包一份runtime && manifest;這夸張的冗余我們自然是不能忍的,那么
用上CommonsChunkPlugin后,runtime && manifest就會統一遷到common chunk了。

runtime && manifestcommon chunk帶來的緩存危機

雖說把runtime && manifest遷到common chunk后,代碼冗余的問題算是解決了,但卻造成另一問題:由于我們在上述的靜態資源的文件名命名上都采用了[chunkhash]的方案,因此也使得只要我們稍一改動源代碼,就會有起碼一個 chunk 的命名會產生變化,這就會導致我們的runtime && manifest也產生變化,從而導致我們的common chunk也發生變化,這或許就是 webpack 規定含有runtime && manifestcommon chunk不能使用[chunkhash]的原因吧(反正chunkhash肯定會變的,還不如不用呢是不是)。

要解決上述問題(這問題很嚴重啊我摔,common chunk怎么能用不上緩存啊,這可是最大的chunk啊),我們就需要把runtime && manifest給獨立出去。方法也很簡單,在用來打包common chunkCommonsChunkPlugin后,再加一CommonsChunkPlugin

  /* 抽取出所有通用的部分 */
  new webpack.optimize.CommonsChunkPlugin({
    name: "commons/commons",      // 需要注意的是,chunk的name不能相同!!!
    filename: "[name]/bundle.[chunkhash].js", // 由于runtime獨立出去了,這里便可以使用[chunkhash]了
    minChunks: 4,
  }),
  /* 抽取出webpack的runtime代碼,避免稍微修改一下入口文件就會改動commonChunk,導致原本有效的瀏覽器緩存失效 */
  new webpack.optimize.CommonsChunkPlugin({
    name: "webpack-runtime",
    filename: "commons/commons/webpack-runtime.[hash].js", // 注意runtime只能用[hash]
  }),

這樣一來,runtime && manifest代碼段就會被打包到這個名為webpack-runtime的 chunk 里了。這是什么原理呢?據說是在使用CommonsChunkPlugin的情況下, webpack 會把runtime && manifest打包到最后面的一個CommonsChunkPlugin生成的 chunk 里,而如果這個chunk沒有其它代碼,那么自然就達到了把runtime && manifest獨立出去的目的了。

需要注意的是,如果你用了html-webpack-plugin來生成html頁面,記得要把這runtime && manifest的 chunk 插入到html頁面上,不然頁面報錯了可不怪我哦。

至此,由于runtime && manifest獨立出去成一個chunk了,于是common chunk的命名便可以使用[chunkhash]了,也就是說,common chunk現在也能做到公共模塊內容有更新了,才更新文件名;另一方面,這個獨立出去的 runtime && manifest chunk,是每次 webpack 打包構建的時候都會更新了。

有必要把 manifest 從 runtime && manifest chunk 中獨立出去嗎?

是的,不用驚訝,的確是有這么一個騷操作。

把 manifest 獨立出去的理由是這樣的:manifest 獨立出去后,runtime 的部分基本上就不會有變動了;到這里,我們就知道,runtime && manifest里實際上就是 manifest 在變;因此把 manifest 獨立出去,也是進一步地利用瀏覽器緩存(可以把 runtime 的緩存保留下來)。

具體是怎么做的呢?主流有倆方案:

利用chunk-manifest-webpack-plugin把 manifest 生成一個json文件,然后由 webpack 異步加載。

如果你是用html-webpack-plugin來生成html頁面的話,還可以利用inline-chunk-manifest-html-webpack-plugin(html-webpack-plugin作者推薦)來把manifest直接輸出到html頁面上,這樣就能省一個 Http 請求了。

我試用過第二種方案,好使,但最終還是放棄了,為什么呢?

把 manifest 獨立出去后,只剩下 runtime 的 chunk 的命名還是只能用[hash],而不能利用[chunkhash],這就導致我們根本沒法利用瀏覽器緩存。后來,我又想出一個折衷的辦法,連[hash]也不要了,直接寫死一個文件名;這樣的話,的確瀏覽器緩存就能保存下來了。但后來我還是反轉了自己,這種方法雖然能留下瀏覽器緩存,卻做不到“該去則去”。或許大家會有疑問,你不是說 runtime 不會變的嗎,那留下緩存有什么關系呀?是的,在同一 webpack 環境下 runtime 的確不會變,但難保 webpack 環境改變后,這runtime會怎么樣呀。比如說 webpack 的版本升級了、 webpack 的配置改了、loader & plugin 的版本升級了,在這些情況下,誰敢保證 runtime 永遠不會變啊?這 runtime 一用錯了過期的緩存,那很可能整個系統都會崩潰的啊,這個險我實在是冒不起,所以只能作罷。

不過我看了下Array-Huang/webpack-seed的runtime && manifest chunk,也才 2kb 而已嘛,你們管好自己的強迫癥和代碼潔癖好嗎?!

緩存問題雜項 模塊id帶來的緩存問題

webpack 處理模塊(module)間依賴關系時,需要給各個模塊定一個 id 以作標識。webpack 默認的 id 命名規則是根據模塊引入的順序,賦予一個整數(1、2、3……)。當你在源碼中任意增添或刪減一個模塊的依賴,都會對整個
id 序列造成極大的影響,可謂是“牽一發而動全身”了。那么這對我們的瀏覽器緩存會有什么樣直接的影響呢?影響就是會造成,各個chunk中都不一定有實質的變化,但引用的依賴模塊id卻都變了,這明顯就會造成 chunk 的文件名的變動,從而影響瀏覽器緩存。

webpack 官方文檔里推薦我們使用一個已內置進 webpack2 里的 plugin:HashedModuleIdsPlugin,這個 plugin 的官方文檔在這里。

webpack1 時代便有一個NamedModulesPlugin,它的原理是直接使用模塊的相對路徑作為模塊的 id,這樣只要模塊的相對路徑,模塊 id 也就不會變了。那么這個HashedModuleIdsPlugin對比起NamedModulesPlugin來說又有什么進步呢?

是這樣的,由于模塊的相對路徑有可能會很長,那么就會占用大量的空間,這一點是一直為社區所詬病的;但這個HashedModuleIdsPlugin是根據模塊的相對路徑生成(默認使用md5算法)一個長度可配置(默認截取4位)的字符串作為模塊的 id,那么它占用的空間就很小了,大家也就可以安心服用了。

To generate identifiers that are preserved over builds, webpack supplies the NamedModulesPlugin (recommended for development) and HashedModuleIdsPlugin (recommended for production).

從上可知,官方是推薦開發環境用NamedModulesPlugin,而生產環境用HashedModuleIdsPlugin的,原因似乎是與熱更新(hmr)有關;不過就我看來,僅在生產環境用HashedModuleIdsPlugin就行了,開發環境還管啥瀏覽器緩存啊,俺開 chrome dev-tool 設置了不用任何瀏覽器緩存的。

用法也挺簡單的,直接加到plugin參數就成了:

plugins: {
  // 其它plugin
  new webpack.HashedModuleIdsPlugin(),  
}
由某些 plugin 造成的文件改動監測失敗

有些 plugin 會生成獨立的 chunk 文件,比如CommonsChunkPluginExtractTextPlugin(從js中提取出css代碼段并生成獨立的css文件) 。

這些 plugin 在生成 chunk 的文件名時,可能沒料想到后續還會有其它 plugin (比如用來混淆代碼的UglifyJsPlugin)會對代碼進行修改,因此,由此生成的 chunk 文件名,并不能完全反映文件內容的變化。

另外,ExtractTextPlugin有個比較嚴重的問題,那就是它生成文件名所用的[chunkhash]是直接取自于引用該css代碼段的 js chunk ;換句話說,如果我只是修改 css 代碼段,而不動 js 代碼,那么最后生成出來的css文件名依然沒有變化,這可算是非常嚴重的瀏覽器緩存“該去不去”問題了。
2017-07-26 改動:改用[contenthash]便不會出現此問題,上見css部分

有一款 plugin 能解決以上問題:webpack-plugin-hash-output。

There are other webpack plugins for hashing out there. But when they run, they don"t "see" the final form of the code, because they run before plugins like webpack.optimize.UglifyJsPlugin. In other words, if you change webpack.optimize.UglifyJsPlugin config, your hashes won"t change, creating potential conflicts with cached resources.

The main difference is that webpack-plugin-hash-output runs in the last compilation step. So any change in webpack or any other plugin that actually changes the output, will be "seen" by this plugin, and therefore that change will be reflected in the hash.

簡單來說,就是這個webpack-plugin-hash-output會在 webpack 編譯的最后階段,重新對所有的文件取文件內容的 md5 值,這就保證了文件內容的變化一定會反映在文件名上了。

用法也比較簡單:

plugins: {
  // 其它plugin
  new HashOutput({
    manifestFiles: "webpack-runtime", // 指定包含 manifest 在內的 chunk
  }),
}
總結

瀏覽器緩存很重要,很重要,很重要,出問題了怕不是要給領導追著打。另外,這一塊的細節特別多,必須方方面面都顧到,不然哪一方面出了紕漏就全局泡湯。

示例代碼

諸位看本系列文章,搭配我在Github上的腳手架項目食用更佳哦(笑):Array-Huang/webpack-seed(https://github.com/Array-Huang/webpack-seed)。

附系列文章目錄(同步更新)

webpack多頁應用架構系列(一):一步一步解決架構痛點

webpack多頁應用架構系列(二):webpack配置常用部分有哪些?

webpack多頁應用架構系列(三):怎么打包公共代碼才能避免重復?

webpack多頁應用架構系列(四):老式jQuery插件還不能丟,怎么兼容?

webpack多頁應用架構系列(五):聽說webpack連less/css也能打包?

webpack多頁應用架構系列(六):聽說webpack連圖片和字體也能打包?

webpack多頁應用架構系列(七):開發環境、生產環境傻傻分不清楚?

webpack多頁應用架構系列(八):教練我要寫ES6!webpack怎么整合Babel?

webpack多頁應用架構系列(九):總有刁民想害朕!ESLint為你阻擊垃圾代碼

webpack多頁應用架構系列(十):如何打造一個自定義的bootstrap

webpack多頁應用架構系列(十一):預打包Dll,實現webpack音速編譯

webpack多頁應用架構系列(十二):利用webpack生成HTML普通網頁&頁面模板

webpack多頁應用架構系列(十三):構建一個簡單的模板布局系統

webpack多頁應用架構系列(十四):No復制粘貼!多項目共用基礎設施

webpack多頁應用架構系列(十五):論前端如何在后端渲染開發模式下夾縫生存

webpack多頁應用架構系列(十六):善用瀏覽器緩存,該去則去,該留則留

本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。
原文地址:https://segmentfault.com/a/1190000010317802
如果您對本系列文章感興趣,歡迎關注訂閱這里:https://segmentfault.com/blog/array_huang

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/61889.html

相關文章

  • webpack多頁應用架構系列十六):善用覽器緩存該去則去該留則留

    摘要:瀏覽器緩存簡單介紹下面來簡單介紹一下瀏覽器緩存,以及為何我要在標題中強調該去則去,該留則留。但后來我還是反轉了自己,這種方法雖然能留下瀏覽器緩存,卻做不到該去則去。 本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。原文地址:https://segmentfault.com/a/1190000010317802如果您對本系列文章感興趣,歡迎關注訂閱這里:h...

    娣辯孩 評論0 收藏0
  • webpack多頁應用架構系列(三):怎么打包公共代碼才能避免重復?

    摘要:在上一篇文章多頁應用架構系列二配置常用部分有哪些中,我介紹了如何配置多頁應用的入口,然而,如果僅僅如此操作,帶來的后果就是,打包生成出來的每一個入口文件都會完整包含所有代碼。的初始化常用參數有哪些,給這個包含公共代碼的命個名唯一標識。 本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。原文地址:https://segmentfault.com/a/1190...

    oliverhuang 評論0 收藏0
  • webpack多頁應用架構系列(一):一步一步解決架構痛點

    摘要:本文首發于的技術博客實用至上,非經作者同意,請勿轉載。原文地址如果您對本系列文章感興趣,歡迎關注訂閱這里這系列文章講什么本系列文章主要介紹如何用這一當前流行的構建工具來設計一個多頁應用的架構。 本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。原文地址:https://segmentfault.com/a/1190000006843916如果您對本系列文章...

    Lowky 評論0 收藏0
  • 2017-07-24 前端日報

    摘要:前端日報精選刺破的心臟之響應式源碼分析語法樹與代碼轉化實踐,,的區別被多數人誤解的可編程代碼說明文檔的幾種選擇中文技術周刊期中的變量和作用域學習筆記教程中的掘金又一個模板引擎掘金的雙向性學習筆記十之掘金學習筆記源碼閱讀掘 2017-07-24 前端日報 精選 刺破 Vue 的心臟之——響應式源碼分析JavaScript 語法樹與代碼轉化實踐IaaS,PaaS,SaaS 的區別被多數人誤...

    ACb0y 評論0 收藏0
  • webpack多頁應用架構系列(九):總有刁民想害朕!ESLint為你阻擊垃圾代碼

    摘要:本文首發于的技術博客實用至上,非經作者同意,請勿轉載。如果你使用了,或類似的,那么,通過編譯前后的代碼相差就很大了,這會造成兩個問題以為例把你的代碼轉成什么樣你自己是無法控制的,這往往導致無法通過的審查。 本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。原文地址:https://segmentfault.com/a/1190000007030775如果您...

    dingding199389 評論0 收藏0

發表評論

0條評論

pekonchan

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<