摘要:可能會延長這些的壽命假設你有以下的這個緩存了和如果命中了緩存,就從緩存中取,否則發起網絡請求如果我們更改了,我們會修改中的版本號,觸發的更新。
本文翻譯自:https://jakearchibald.com/201...這是一篇2016年的老文章。作者是Chrome瀏覽器的開發成員。
本文首發于公眾號:符合預期的CoyPan
使用正確的緩存可以帶來巨大的頁面性能上的收益,節省帶寬,減少服務器成本。但是許多網站并沒有解決好他們的緩存問題,創造了一個race conditions,導致相互依賴的資源之間失去了同步。
絕大多數緩存的最佳實踐,都屬于下面兩種模式:
模式一:不可變的內容 ,長時間的max-ageCache-Control: max-age = 31536000
同一個URL對應的內容永不改變
瀏覽器/CDN 可以緩存這個資源長達一年的時間
被緩存資源的存儲時間小于max-age指定的秒數時,該資源可以直接被使用而無需經過服務器。
在這種模式下,你不會去改變特定url下的文件內容,你直接改變url:
每一個URL都包含一個跟隨文件內容變換的部分。這個部分可以是版本號,修改日期,或者文件內容的hash值。
大多數服務端框架都有工具可以簡單的實現這個需求。Node.js下還有更輕量級的工具能夠做到同樣的事情,比如gulp-rev.
但是,這種模式不適合諸如文章、博客這樣的場景。文章和博客的URL是不會有版本號的,而且他們的內容能夠隨時修改。說真的,如果我在文章中犯了拼寫或者語法錯誤,那么我需要能夠快速、頻繁的修改文章內容。
模式二:可變的內容,總是向服務器發起校驗Cache-Control: no-cache
同一個url對應的內容會改變
任何本地緩存的版本都是不可信的,除非服務器校驗通過
注意:no-cache并不意味著不緩存,而是使用緩存前必須請求服務端進行檢查(或者說叫重新校驗)。no-store告訴瀏覽器,根本不要緩存這個文件。同時,must-revalidate也不是說就『must-revalidate』,而是如果本地資源的緩存時間還沒有超過設置的max-age的值,就可以直接使用本地資源,否則必須重新校驗。
在這種模式下,你可以在響應頭里添加一個ETag(你選擇的版本ID)或者Last-Modified。客戶端下一次請求資源時,會分別帶上If-None-Match和If-Modified-Since,服務端會判斷說:直接使用你已有的本地資源吧,他們是最新的。這就是最常見的:HTTP 304
如果沒有帶上ETag/Last-Modified,服務端會再次返回完成的內容。
這種模式總是會發起一個網絡請求,而模式一是可以不用通過網絡的。
使用模式一時,因為網絡基礎建設而導致的延時是很常見的,使用模式二時,也很容易遇到網絡環境帶來的延遲。取而代之的是中間的東西:一個短時間的max-age設置和可變的內容。這是一種十分糟糕的妥協。
對可變內容使用max-age通常是一個錯誤的選擇不幸的是,這種做法并非不常見。比如,Github pages就是這樣的。
想象一下有以下三個url:
/article/
/styles.css
/scripts.js
服務端都是返回的:
Cache-Control: must-revalidate, max-age=600
url對應的內容是變了
如果瀏覽器緩存了一個資源版本,但是沒有到10分鐘,會不經過服務器直接使用這個緩存的資源。
否則發起一個網絡請求,帶上If-Modified-Since或者If-None-Match(如果可用)
這種模式在測試的時候看起來是可以的,但在現實中,會出問題,并且很難追蹤。在上面的例子中,服務端確實已經更新了HTML, CSS 和JS,但是頁面最終使用了緩存里的HTML,JS,CSS卻是從服務端獲取的最新的版本。資源版本不匹配導致了頁面出錯。
通常情況下,當我們對HTML進行重大更改時,我們還可能更改HTML對應的CSS結構,并更新JS以適應樣式和內容的更改。這些資源是相互依賴的,但是緩存的header是無法描述這種依賴的。用戶最終看到的,可能是一兩個新版本的資源,和其他老的資源。
max-age和響應時間有關,因此,如果上述所有的資源都是在同一次訪問中請求的,他們大概會在同一時間到期,但是仍然有很小的可能發生競爭。如果你的某些頁面并不包含JS或者包含了不同的CSS,那么過期時間可能就不同步了。更糟糕的是,更糟糕的是,瀏覽器總是從緩存中刪除東西,它不知道HTML、CSS和JS是相互依賴的,所以它會很高興地刪除一個而不是其他的。上述的情況,都可能會導致頁面資源的版本不匹配。
對用戶來說,他們最終會看到錯誤的頁面布局和錯誤的頁面功能,從細微的錯誤到完全不可用的內容。
謝天謝地,對用戶來說還是有補救措施的。
刷新可能會修復這個問題
如果頁面作為刷新的一部分加載,瀏覽器會忽略max-age,向服務器進行驗證。因此,如果用戶遭遇了因為max-age而造成的錯誤,刷新是可以解決問題的。當然,強迫用戶這樣做會降低信任度,因為這會讓你感覺到你的網站是不靠譜的。
service worker可能會延長這些bug的壽命
假設你有以下的service worker:
const version = "2"; self.addEventListener("install", event => { event.waitUntil( caches.open(`static-${version}`) .then(cache => cache.addAll([ "/styles.css", "/script.js" ])) ); }); self.addEventListener("activate", event => { // …delete old caches… }); self.addEventListener("fetch", event => { event.respondWith( caches.match(event.request) .then(response => response || fetch(event.request)) ); });
這個service-worker
緩存了script和style
如果命中了緩存,就從緩存中取,否則發起網絡請求
如果我們更改了CSS/JS,我們會修改service-worker中的版本號,觸發service-worker的更新。但是,假如addAll發出的請求經過了HTTP緩存(和其他大多數緩存一樣),我們也會進入到max-age的race condition,緩存不匹配的CSS、JS版本。
一旦他們被緩存了,我們將會一直看到不匹配的CSS和JS,直到我們下一次更新service-worker。而在下一次更新時,我們可能還會陷入另一個race condition。
你可以在service worker中跳過緩存:
self.addEventListener("install", event => { event.waitUntil( caches.open(`static-${version}`) .then(cache => cache.addAll([ new Request("/styles.css", { cache: "no-cache" }), new Request("/script.js", { cache: "no-cache" }) ])) ); });
不幸的是,這個緩存的設置在Chrome/Opera中還不支持,Firefox也是剛剛支持。你可以自己來實現類似的功能:
self.addEventListener("install", event => { event.waitUntil( caches.open(`static-${version}`) .then(cache => Promise.all( [ "/styles.css", "/script.js" ].map(url => { // cache-bust using a random query string return fetch(`${url}?${Math.random()}`).then(response => { // fail on 404, 500 etc if (!response.ok) throw Error("Not ok"); return cache.put(url, response); }) }) )) ); });
在上述代碼中,我用隨機數來避免緩存,但是你可以更進一步,在構建的時候為內容增加一個hash值(和sw-precache做的事差不多)。這是一種在js層面的對模式一的實現,但是僅僅對service worker的使用者是有效的,而不是對所有的瀏覽器和你的CDN都有效。
service worker & http緩存可以同時使用,不要讓他們沖突正如你所見,你可以繞過service worker中糟糕的緩存,但是你最好解決根源的問題。正確的設置緩存能夠讓你在使用service worker的時候更加輕松,并且對那些不支持service worker的瀏覽器也是有好處的,還能讓你充分的使用你的CDN。
正確的緩存頭還意味著你可以大量簡化server worker的更新:
const version = "23"; self.addEventListener("install", event => { event.waitUntil( caches.open(`static-${version}`) .then(cache => cache.addAll([ "/", "/script-f93bca2c.js", "/styles-a837cb1e.css", "/cats-0e9a2ef4.jpg" ])) ); });
在這里,我將使用模式2(服務器重新驗證)緩存根頁面,其余資源使用模式1(不可變內容)。每次service worker更新都將觸發對根頁面的請求,但只有當資源的URL發生更改時,才會下載其余資源。這很好,因為無論你是從以前的版本還是第10個版本更新,它都可以節省帶寬并提高性能。
相對于本地應用來說,這是一個巨大的優勢。在本地應用中,不管二進制內容有細微和巨大的改變,整個二進制內容都會被下載。而在這里,我們只需要一個小小的下載,就能更新巨大的web app.
service worker的工作最好是作為一個增強方案,而不是變通方案。所以預期與緩存抗爭,不如好好利用緩存。
謹慎使用,max-age & 可變內容 也可以很有效對于可變內容使用max-age一般情況下是一個錯誤的選擇,但也不總是這樣。比如,這個頁面設置了一個3分鐘的max-age. race condition在這個頁面是不會成為問題的,因為這個頁面沒有任何遵循這一種模式的依賴(我的css,js,圖片等都遵循模式1-不可變內容),依賴于此頁的任何內容都不會遵循相同的模式。
這種模式意味著,如果我有幸寫了一篇熱門文章,我的cdn可以讓我的服務器散熱,而我能忍受用戶需要花三分鐘時間才看到文章更新。
這種模式不能隨便使用。如果我在文章中添加了一個新的部分,并且將這個部分鏈接到一篇新的文章,那么我就創造了一個會爭用的依賴項。用戶可以單擊鏈接,并在沒有引用部分的情況下獲取文章的副本。如果我想避免這種情況,我就得更新第一篇文章,刷新cdn, 等待3分鐘,然后在另一篇文章中添加指向他的鏈接。是的…..你必須非常小心這種模式。
正確使用,緩存能極大的提高性能并且較少帶寬消耗。對于任何容易更改的URL,都支持不可變的內容,否則在服務器重新驗證時會使其安全。只有當你足夠勇敢,并且你確信你沒有可能會失去同步的依賴項時,再使用max-age和可變內容的模式。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/62070.html
摘要:大部分服務器端的框架會有一些工具來讓這件事情變得簡單,可是,這種方式并不適用于一些文章或者博客之類的網站,這些網站的不能通過版本號來管理,而且內容也是經常需要改變的。指不允許緩存。 本文譯自: 這里 本文已同步到我的博客 引言 緩存利用得當的話,有很大益處,比如節省帶寬,降低服務器壓力等,但是很多網站沒能夠很好地利用緩存,造成一些相互依賴的資源出現不同步的情況。 關于緩存的處理方案主要...
摘要:前端靜態資源緩存最優解以及的陷阱合理的使用緩存可以極大地提高網站的性能優勢,還可以節約帶寬從而降低服務器成本。此處注意和與第一天請求的版本號不同。既支持版本號類型的靜態資源緩存方式也支持服務器重新認證的方式。 前端靜態資源緩存最優解以及max-age的陷阱 合理的使用緩存可以極大地提高網站的性能優勢,還可以節約帶寬從而降低服務器成本。但是很多站點有只弄對了一半或者一半都沒有,如果是這樣...
摘要:業界動態發布版本,同時發布了版本以及首個穩定版本的。程序人生如何用人類的方式進行二關于如何在中進行良好的溝通,避免陷入一些潛在的陷阱。技術周刊由小組出品,匯聚一周好文章,周刊原文。 業界動態 Angular 5.1 & More Now Available Angular發布5.1版本,同時發布了Angular CLI 1.6版本以及首個穩定版本的Angular Material。CL...
摘要:記錄以下資源備忘也就是本文譯文標題為意譯,原標題為,恐有不當,特此說明。譯者注翻譯本文時譯者使用的確實無法看到信息,建議使用最新金絲雀版本一探究竟。應用程序不應當依賴于服務器推送的可用性及其使用。 本文轉載自:眾成翻譯譯者:文藺鏈接:http://www.zcfy.cc/article/883原文:https://blog.cloudflare.com/http-2-server-pu...
閱讀 2565·2021-10-11 10:58
閱讀 1148·2021-09-29 09:34
閱讀 1486·2021-09-26 09:46
閱讀 3830·2021-09-22 15:31
閱讀 730·2019-08-30 15:54
閱讀 1458·2019-08-30 13:20
閱讀 1251·2019-08-30 13:13
閱讀 1486·2019-08-26 13:52