祐成

頁面載入太慢怎麼辦?先給舊資料,背景再更新

蒸餾精華第 2 段講快取策略。我追問了「舊資料是誰的」和「能不能用 Playwright 預熱快取」,搞懂了四個層次的快取設計。

朱祐成
· 9 分鐘閱讀 · 學習筆記
頁面載入太慢怎麼辦?先給舊資料,背景再更新
Image courtesy of The Metropolitan Museum of Art, Open Access

竇加《舞蹈課》(The Dance Class),1874 年。竇加(Edgar Degas)以偏離中心的構圖捕捉芭蕾排練室的真實瞬間,舞者們在角落練習、休息、整理衣裙,編舞大師朱爾・佩羅持杖指導。此畫融合印象派的光影觀察與古典素描的精準,是竇加芭蕾系列中最著名的作品之一。現藏於紐約大都會藝術博物館。

使用者點開你的頁面,等了三秒還是空白,然後關掉走人。問題不是你的資料不存在,而是你堅持等「最新的」才顯示。其實你手上有一份舊資料,雖然過期了,但比空白好一萬倍。


Stale-While-Revalidate 快取策略

核心觀念只有一句話:過期的資料 > 空白的等待。

使用者要資料

  ├─ 有快取(即使過期)→ 立刻回傳,畫面秒出
  │                       同時背景去抓最新的 → 存起來

  └─ 沒快取 → 同步抓取(只有第一次要等)

快取分三層,越上面越快:

層級速度特性
Memory(記憶體)最快程式還在跑就有,重啟就消失
File(檔案)程式重啟還在,換機器就沒了
Network(網路)最慢真的去打 API,但拿到最新的

四個層次的快取設計

追問「舊資料是誰的」和「第一次能不能也不等」之後,整理出完整的演進路線:

策略第一個人要等嗎說明
沒快取每次都等最簡單,最慢
Stale-While-Revalidate第一次要等之後都秒開
伺服器端共享快取最最最第一次要等所有人共享同一份
+ 快取預熱沒人需要等部署時先跑腳本填好快取

我的追問與發現

「所有人看到同一份舊資料?還是各自看各自的?」

看快取放哪裡。放用戶端(瀏覽器、本機),每個人第一次都要等,各存各的。放伺服器端(Cloudflare KV 之類),所有人共享同一份快取,只有最最最第一次要等。

「伺服器端的『最最最第一次』,我不能先用腳本創造快取嗎?」

可以,這叫快取預熱(Cache Warming)。部署完成後、開放流量前,跑一個腳本主動打 API 把結果寫進快取。不需要 Playwright 那麼重的工具——除非你的快取需要經過瀏覽器渲染(例如截圖),否則一個 fetch 腳本就夠了。


你可以怎麼用

  • 找出你的網站或工具裡「每次都要等 API 回應」的頁面
  • 加一層檔案快取:第一次抓到的結果存下來,下次直接用,背景更新
  • 如果是多人使用的服務,把快取從用戶端搬到伺服器端(Cloudflare KV、Redis)
  • 部署流程加一步「預熱」:部署完先跑腳本填快取,再開放流量

延伸連結


一句話帶走

過期的資料比空白的等待好一萬倍——先給舊的,背景再更新。



📚 完整學習對話紀錄(想看完整脈絡可展開)

原文(蒸餾精華 第 2 段)

讓 CLI/UI 感覺即時的快取策略:

Memory(最快) → File(忽略過期) → Network(最慢)

getModelsInstant():
  有快取 → 立即回傳(即使過期)→ 背景靜默更新
  沒快取 → 同步抓取(首次使用)
  網路失敗 → 靜默忽略,用舊資料

Claude 的白話解釋

使用者要資料時,先把舊的立刻給他看,背景再悄悄去抓新的。

使用者要資料

有快取(即使過期了)→ 先回傳舊的,畫面秒出
  ↓ 同時
背景偷偷去 API 抓最新的 → 存起來,下次就是新的了

核心觀念:過期的資料 > 空白的等待。

三層快取的優先順序:

層級速度說明
Memory(記憶體)最快程式還在跑就有
File(檔案)程式重啟還在
Network(網路)最慢真的去打 API

跟第 1 段的關係:第 1 段處理「API 掛了怎麼辦」,第 2 段處理「API 太慢怎麼辦」。一個管可靠性,一個管速度感,互補的。


我的提問:所有人看到同一份舊資料?還是各自看各自的?

這段我想了很久。到底對每一個真實用戶來說:

  • 是大家一打開時共同讀取到一個歷史舊畫面,一有更新就換掉?
  • 還是每個人第一次都要等,第二次才看到自己上回的快取?

Claude 的回答:看快取存在哪裡

兩種都對,取決於快取放的位置:

快取在用戶端(各存各的)

用戶 A 第一次來 → 沒快取 → 等待 API → 存到 A 的快取
用戶 A 第二次來 → 讀 A 自己的快取(秒開)→ 背景更新

用戶 B 第一次來 → 沒快取 → 也要等
用戶 B 第二次來 → 讀 B 自己的快取(秒開)→ 背景更新

蒸餾裡的做法是 CLI 工具,快取在每個人自己電腦上,不共享。

快取在伺服器端(所有人共享)

伺服器每隔一段時間更新快取

用戶 A 第一次來 → 讀伺服器快取(秒開)→ 沒人需要等
用戶 B 第一次來 → 讀同一份快取(也秒開)

如果是網站服務(放在 Cloudflare KV 之類的),就能做到所有人都看同一份快取。

快取放哪第一個人要等嗎大家共享嗎
用戶端(本機)每人第一次都要等不共享,各存各的
伺服器端(共享)只有最最最第一次要等共享,大家看同一份

核心精神不變:寧可先給舊的,也不要讓人乾等。差別只在快取放的位置決定了「誰的舊」。


我又追問:伺服器端的「最最最第一次」,我不能用 AI 跑 Playwright 先創造快取嗎?

既然可以自動化,為什麼要讓任何人等?


Claude 的回答:可以,這叫快取預熱

完全可以。這就叫快取預熱(Cache Warming):服務上線前,先用腳本跑一遍,把快取預先填好。連「最最最第一次」都不用等。

部署流程:
1. 程式碼部署完成
2. 跑預熱腳本 → 主動打一遍 API → 把結果寫進快取
3. 開放流量
→ 所有用戶從第一秒起都有快取可讀,零等待

用 Playwright 可以,但其實不需要那麼重。Playwright 是模擬瀏覽器,適合測 UI。預熱快取只需要打 API 拿資料,一個簡單的 fetch 腳本就夠了。除非你的快取內容需要經過瀏覽器渲染才能產生(例如截圖、預覽圖),那 Playwright 才是對的工具。

完整的圖變成:

策略第一個人要等嗎
沒快取每次都等
Stale-While-Revalidate第一次要等
伺服器端共享快取最最最第一次要等
+ 預熱沒人需要等

我學到的

一個快取策略,因為追問了「舊資料是誰的」和「能不能預熱」,從單純的技術知識變成了四個層次的設計選擇。最大的收穫是:技術方案不是固定的,你的追問決定了它能走多遠。