Cache 是什麼
我們在 Browser 上進入一個網站時看到的畫面,是由很多的 Image、Dom、Videos 等等組合來的,我們在上面的操作也都依賴執行要用的 JS、CSS 等檔案,而剛剛提到的全部 Files 初始都存在 Server 上,而不再我們自己的 Client 所以會去跟 Server 要,執行的流程大致上如下圖:
但如果每次都全部重拿是很浪費效能的,且時間比較久,所以 Cache 在這時候就派上用場了,簡單來說就是為了減省 Client 去跟 Server 要 Source 的流量 & 提高 Render Page 的效能所採用的方法,以上圖來說就是優化甚至略過 2~4 的步驟,特別是 Static Source (html、js、css、image 等...),通常我們對 Static Source 效能在意的 2 個點,如下:
- Load Static Source 的速度
- Page Render 的速度
試想看看,如果每次打開同一個網站,比方說 YouTube 好了,每張看過的圖都全部重新下載一次,效能跟消耗的流量都是過多的浪費, Render 速度一定比用 Cache 來的慢很多,萬一又遇上網速本身就慢的 User,幾乎就是一場悲劇。
Cache 的機制
既然知道了 Cache 存在的目的,接著來看一下對於 Browser 來說 Cache 機制的分類吧! 常見的一共可以分成兩大類
- HTTP Cache
- Data Store
HTTP Cache 可以先假想成是當每次 Client 去跟 Server 要 Source 時,都會先執行的檢查,有點類似 Get Source 的警衛,只要警衛檢查這次要 Render 的 Source 與 Client Cache 沒有差異,就不用再去發 HTTP Request,採用 Client Cache Render 即可,詳細的後面會說。
Data Store 大家就比較熟悉了,它儲存的 Data 可能是已經從 Server 拿到的 Info 了,一般都是在多個頁面中常要使用到的,例如: Cookie、Session 裡面放的那些 Token、Page 語系之類的。
這篇主要會放在 HTTP Cache 的部分,至於 Data Store 會再本系列的另外一篇來說,接著我們就進入本篇重點吧!!
HTTP Cache 介紹
前面提到 HTTP Cache 類似 Get Source 的警衛,而警衛呢其實有兩個分別是 “強 Cache” & “協商 Cache”,它們之間是相輔相成的,那我們先來看看當一個 Get Source 發生時 Client 跟 Server 的溝通,如下圖:
圖中可以看到進入(3) 的時候,最先執行的是 Case 1 之後 才是 Case 2 & 3,這個順序也是 Cache 的執行順序,固定都是 “強 Cache 先,協商 Cache 後” 這點務必牢記,接著我們一點點來細看這兩種機制吧!!
強 Cache :
它的特性是 如果該項 Source 有 Match 到強 Cache,就不發出 HTTP Request 會直接用 Client Cache,若沒有 Match 到就會進到協商 Cache 階段,而判斷的依據是通過 Expires 和 Cache-Control 兩個 Respond Header Tag 來實現檢查 Source 有無過期,如下:
1. Expires :
它是早期 HTTP 1.0 時提出的方案,用來表示 Source 過期時間的 String,可以在 Respond Header 中找到。
長相 :
Expires: Mon, 15 Jul 2019 16:22:58 GMT
範例表示該項 Source 在 2019年7月15日 4點 22分 58秒之前的 Source 來源都是可以用 Client Cache 的。
如果我們打開 Chrome DevTools 來看的話,就如下圖
特性 :
- Browser 在第一次跟 Server 拿某個 Source 的時候, Server 會紀錄時間,之後在 Respond Header 中加上這個 Tag 回給 Browser (像上圖那樣)。
- Browser 收到之後,會把 Source & 這次所有 Respond Header 一起存起來。
- 之後當強 Cache 有 Match 到時,那次 Request 的 Respond Header 並不是來自 Server,而是來自之前的 Cache 存起來的 Respond Header。
- 第二次之後當 Browser 又要拿這項 Source 時,就會直接先找 Client Cache,找到的話就比較當下時間跟 Cache 中的 Expires Tag String。
- 若是比較後時間沒過期,就直接用 Cache 的即可。
- 若已經過期的話,就要向 Server 重新發送一次 HTTP Request 拿新的,會再執行一次剛剛說到的第一點 & 第二點。
缺點 :
- Expires Tag 的 String 是絕對時間,但 Client 與 Server 兩端的時間不一定是 Sync 的,如果 User 在 Client 上修改了自己 Local 的時間,那兩端對不起來,就會造成 Cache 功能失效。
也因為 Expires 兩端時間 Sync 容易對不上的這項缺點,所以在 HTTP 1.1 出現了 Cache-Control,我們接著來看看吧!!
2. Cache-Control :
為了改上前面 Expires 問題而出現的新 Tag,它改用了相對時間來判斷 Cache 是否過期,你也可以在 Respond Header 中找到它。
長相 :
Cache-Control:max-age=31536000
範例表示當 Clint 收到上面這個 Respond ,代表在往後的 31536000 秒 (1年) 之內,該項 Source 都可以拿取 Client Cache 來做使用。
如果我們打開 Chrome DevTools 來看的話,就如下圖:
特性 :
- Browser 在第一次跟 Server 拿某個 Source 的時候, Server 會紀錄時間,之後在 Respond Header 中加上這個 Tag 回給 Browser (像上圖那樣)。
- Browser 收到之後,會把 Source & 這次所有 Respond Header 一起存起來。
- 之後當強 Cache 有 Match 到時,那次 Request 的 Respond Header 並不是來自 Server,而是來自之前的 Cache 存起來的 Respond Header。
- 第二次之後當 Browser 又要拿這項 Source 時,就會直接先找 Client Cache,找到的話會根據第一次 Request 時間 & Cache-Control 設定的有效期,計算這個 Source 的過期時間,再拿這個時間跟當前的請求時間比較。
- 若是比較後時間沒過期,就直接用 Cache 的即可。
- 若已經過期的話,就要向 Server 重新發送一次 HTTP Request 拿新的,會再執行一次剛剛說到的第一點 & 第二點。
目前看到這邊會發現 Expires & Cache-Control 只有在計算時間上的部分有差異,接著我們再補充一些對於兩者的小知識點:
Additional Tips:
- Expires & Cache-Control 這兩個 Cache 警衛 可以只啟用一個,也可以同時啟用。
- 當兩個 同時啟用時,Cache-Control 優先級高於 Expires,Browser 會先用 Cache-Control 去檢查比對時間。
至於好奇詳細為什麼會這樣設定的小夥伴可以看[RFC2616] 的定義,這邊就不做多餘的展開了。
協商 Cache :
它的特性是 如果強 Cache 沒有 Match 到,就會到協商 Cache 來,它會發出 HTTP Request 去問 Server 是否採用 Cache,判斷的依據是 Browser 在 Request Header 中會帶有兩個 Tag,Server 根據這些 Tag,來決定是否使用 Cache,而 Tag 分為 Last-Modified 和 E Tag 兩種,會傳給 Server 比對。
1. Last-Modified :
可以在 Respond Header 中找到,用來表示 Client 上的這項 Cache File 最後的修改日期。
長相 :
Last-Modified: Mon, 09 Jul 2018 02:49:54 GMT
範例表示當 Clint 收到上面這個 Respond ,代表該項 Source 在 2018年 7月 9日 2點 49分 54秒之前都可以拿取 Client Cache 來做使用。
如果我們打開 Chrome DevTools 來看的話,就如下圖:
特性 :
- Browser 在第一次跟 Server 拿某個 Source 的時候, Server會紀錄時間,然後在 Respond Header 中帶上這個 Tag 給 Browser。
- 第二次之後發送的 Tag Name 會變成 If-Modified-Since,就是上一次 Server Respond 的 Last-Modified String。
- Server 拿到 Request Header 中的 If-Modified-Since 後,會跟自己所保存的這項 Source 最後修改時間 做比較。
- 如果 Request Header 的 If-Modified-Since 小於最後修改時間,說明是時候更新了,就會 Respond 更新通知,請 Client 發出 HTTP Request,去拿新版的 Source。
- 如果 Server 比對後,沒有小於最後修改時間,Respond 就返回 304,告訴 Browser 可以直接用 Client Cache。
缺點 :
- 如果在 Client 打開 Cache File 即便沒有修改,也會造成 Last-Modified 被判定為修改過,導致又發出 HTTP Request。
- 某些 File 也許會周期性的更改,但是他的內容並沒變(僅僅改變的修改時間),這時候也不用再發出 HTTP Request 去拿新版。
- Last-Modified 能偵測的時間細度是到秒而已,如果 Cache File 在1 秒內被改了很多次,即使這樣 Last-Modified 並不會判定為修改過。
也因為上面這些缺點,所以在 HTTP 1.1 出現了 E Tag,我們接著來看看吧!!
2. E Tag :
它是 Server 根據當前收到的 Cache File Content,給 File 生成的 Key,一旦裡面的 Content 有變動,這個 Key 就會一起變,只要 Browser 收到變動通知就去拿新版的 Source。
長相 :
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" (它是亂數產生的)
ETag: W/"0815"
範例表示當 Clint 收到上面這個 Respond ,代表該項 Source 比對之後,Key沒變都可以拿取 Client Cache 來做使用。
如果我們打開 Chrome DevTools 來看的話,就如下圖:
特性 :
- Browser 在第一次拿取該項 Source 時,Server 的 Respond 中就會帶有這個 Tag (Key)。
- 第二次之後的拿取 Request Header 會帶有一個 Tag Name 叫 If-None-Match,它就是上次 Server 的 Respond 的那個 Key。
- Server 拿到 Request Header 中的 If-None-Match 後,會跟自己所保存的這項 Source 的 Key 做比較。
- 如果 Server 比較後 Key 有變,表示有更新了,就會 Respond 更新通知,請 Client 再發出 HTTP Request,去拿新版的 Source。
- 如果 Server 比對後,Key 沒變 Respond 就返回 304,告訴 Browser 可以直接用 Client Cache。
Additional Tips:
以上 E Tag 的特性算是補足了 Last-Modified 的缺陷部分,看到這裡你也會出現一個疑問這兩者誰的權重比較高呢?
- E Tag 比較高,若有 E Tag 存在 Browser 會先以它為優先比較。
我們來總結一下,當一個 Browser Get Source 發生時,Cache 機制的執行順序吧!
Step 1. Cache-Control (強 Cache)
Step 2. Expires (強 Cache)
Step 3. If-None-Match - E Tag (協商 Cache)
Step 4. If-Modified-Since - Last-Modified (協商 Cache)
Step 5. 都沒 Match 到 or 確認過期後,發出 HTTP Request 拿新版 Source
以上就是 Browser Cache 的第一篇,其實本篇在 Cache 相關的領域中只能算是最初階的入門文,而且文中也有一些細節還沒有說,例如: Status code 的區別、Cache-Control 其他的控制屬性 (private、no-cache、no-store...) 之類的後續會再展開另外一篇來說明,最後希望這篇對大家入門有幫助啦!!