Astro 的路由快取會儲存隨選轉譯頁面的已轉譯回應,因此快取命中時可以略過轉譯。EmDash 會為頁面所轉譯的內容提供快取標籤,並在內容變更時使這些標籤失效。頁面可以快取數天,而發佈的內容通常在幾秒內就能被訪客看到。
物件快取儲存的是查詢結果,每個頁面仍然需要轉譯。設定了快取提供者之後,匿名轉譯會略過物件快取並直接從資料庫讀取,這樣為已失效頁面重新填充快取的那次轉譯就不會來自較舊的快照(參見快取的內容)。接著由路由快取為匿名訪客提供服務,而物件快取主要用來加快已登入編輯者的轉譯。
選擇快取提供者
路由快取需要一個快取提供者。請使用與你的部署相符的提供者:
| 部署方式 | 提供者 | 匯入來源 |
|---|---|---|
| Node.js | memoryCache() | astro/config |
| Cloudflare | cacheCloudflare() | @astrojs/cloudflare/cache |
memoryCache() 把項目保存在伺服器程序的記憶體中。cacheCloudflare() 是一個 CDN 提供者:它把回應儲存在 Cloudflare Workers Cache 中,快取命中時根本不會執行 Worker。Astro 的 Netlify 和 Vercel 轉接器同樣提供 CDN 提供者;參見轉接器快取提供者。EmDash 透過 Astro 與提供者無關的 cache.invalidate() API 使快取失效,因此它的標籤適用於任何提供者。提供者差異介紹了取決於提供者的行為。astro dev 從不快取,因此請針對正式環境建置來測試快取。
設定路由快取
註冊提供者,並宣告哪些路由可以被快取、快取多久,以及使用哪些標籤。此範例使用 Cloudflare 提供者;在 Node 上,請使用 cache: { provider: memoryCache() },並從 astro/config 匯入 memoryCache:
import { defineConfig } from "astro/config";
import { cacheCloudflare } from "@astrojs/cloudflare/cache";
export default defineConfig({
// ...你現有的 output、轉接器和整合
cache: {
provider: cacheCloudflare(),
},
routeRules: {
"/": { maxAge: 86_400, swr: 604_800, tags: ["posts", "pages", "all"] },
"/blog": { maxAge: 86_400, swr: 604_800, tags: ["posts", "all"] },
"/blog/[slug]": { maxAge: 86_400, swr: 604_800, tags: ["posts", "all"] },
"/sitemap.xml": { maxAge: 86_400, swr: 604_800, tags: ["posts", "pages", "all"] },
"/sitemap-[collection].xml": { maxAge: 86_400, swr: 604_800, tags: ["posts", "pages", "all"] },
},
});
maxAge是快取在路由重新轉譯之前提供該項目的時間長度。EmDash 會在發佈時使過期項目失效(見下文),但失效操作可能失敗,因此maxAge同時也限定了頁面最多可以保持過期多久,外加在swr重新驗證期間回傳的那一次過期回應。選擇較長的值之前,請先查看提供者差異。swr讓快取在背景重新驗證的同時繼續提供過期項目,因此頁面一旦被快取,訪客幾乎不必等待轉譯。tags為項目命名,以便使其失效。請用集合名稱為每個路由加上它所轉譯的集合標籤:EmDash 內建的失效機制使用集合名稱,因此相符的標籤會被自動失效。在每個快取路由上再加一個寬泛的額外標籤(這裡是all,它不是集合名稱,所以 EmDash 自己永遠不會使它失效),可以作為緊急開關:使它失效,例如在你自己保護的端點中呼叫context.cache.invalidate({ tags: ["all"] }),就能一次清除所有內容。
查詢輔助函式還會回傳一個 cacheHint,你可以在頁面內將它傳給 Astro.cache.set()。它的標籤指明了確切的項目(getEmDashEntry)、集合(getEmDashCollection)或頁面外圍資源(getMenuWithCacheHint、getSiteSettingsWithCacheHint、getTaxonomyTermsWithCacheHint、getWidgetAreaWithCacheHint)。這些正是 EmDash 在寫入時使其失效的同一批標籤,因此傳入這些提示的頁面,在它所轉譯的項目或頁面外圍資源被寫入時就會失效,包括其路由規則中沒有列出的那些:
---
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
if (!slug) return Astro.rewrite("/404");
const { entry, cacheHint } = await getEmDashEntry("posts", slug);
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---
Astro.cache.set() 會與路由規則合併,標籤在多次呼叫之間累積。快取提示攜帶標籤但不帶 maxAge,因此頁面快取多久由路由規則決定。
請為每個公開路由提供明確的規則。規則依請求路徑比對,所以請逐一列出你的公開路由:網站根路徑上的剩餘參數,例如 /[...slug],同樣會比對 /_emdash/ 下的管理後台和 API 路由。
發佈時的失效是內建的
只要線上內容發生變化,EmDash 的內容 API 路由就會使路由快取失效:發佈、取消發佈、刪除、從垃圾桶還原項目,以及儲存影響線上項目的變更,都會使標籤 [collection, id] 失效。建立或複製項目不會使任何內容失效,因為新項目是草稿。因此,即使 maxAge 很長,任何帶有集合名稱標籤(如上所示)的路由規則通常也會在發佈後的幾秒內重新整理。
網站外圍內容也在涵蓋範圍內:儲存網站設定會使 emdash:settings 失效,選單編輯會使 emdash:menu:{name} 失效,分類法和小工具區域的寫入會使與之對應的 emdash:taxonomy:{name} / emdash:widget-area:{name} 標籤失效。當頁面使用 getMenuWithCacheHint()、getSiteSettingsWithCacheHint()、getTaxonomyTermsWithCacheHint() 或 getWidgetAreaWithCacheHint() 回傳的 cacheHint 時,會自動取得這些標籤。
選單標籤僅涵蓋選單的編輯。發佈、取消發佈、移入垃圾桶或還原選單所連結的條目,或者變更被連結條目或分類術語的 slug,都不會使該選單的標籤失效。顯示該選單的頁面,如果其標籤中同時包含該條目、其集合或該術語所屬的分類法,就會取得此類變更;否則要等到頁面過期時才會更新。
建立、編輯或刪除重新導向,會使位於該重新導向來源路徑上(帶與不帶結尾斜線)的快取頁面失效,使重新導向在下一次請求時生效。
還原較早的修訂,以及對署名、媒體庫和留言的變更,都不會使任何內容失效。受它們影響的快取頁面會在過期後,或轉譯它們的項目再次被儲存時更新。排程發佈僅在 Cloudflare 上會使已發佈項目的標籤失效;參見提供者差異。
這也是標籤慣例很重要的原因:EmDash 會使集合名稱、項目 id 和 emdash:* 外圍標籤失效。如果你的路由規則使用其他標籤名稱,內建的失效機制將無法比對它們,過期項目會一直保留到 maxAge 到期。
讓回應不進入快取
路由規則依請求路徑比對,因此它們也會作用於你不希望以較長 TTL 快取的回應。EmDash 會自動讓為已登入使用者轉譯的回應、帶有 Cache-Control: private 或 no-store 送出的回應,以及 404 回應不進入路由快取。其他錯誤回應以及你自己的中介軟體重新導向,則由範本負責,透過 Astro.cache.set(false) 依請求選擇退出。
Astro.cache.set(false) 只會保持到下一次 Astro.cache.set() 呼叫為止。版面配置在頁面前置內容之後轉譯,因此,設定了快取提示的版面配置會讓已選擇退出的頁面重新開啟快取。請把退出設定傳給版面配置(例如作為 prop),並在設定了它時略過版面配置中的 Astro.cache.set() 呼叫。
預覽和編輯連結會被自動處理
EmDash 的中介軟體會自行讓這些回應退出路由快取,因此不需要任何範本程式碼:攜帶 ?_preview= 權杖的請求(無論有效與否,因為這些 URL 是依權杖區分的,否則快取的草稿在權杖過期後仍然可以被存取),以及注入了編輯器工具列的回應。使用 toolbar: "client" 時,?_edit URL 同樣適用。有效的預覽回應和注入了工具列的回應還會額外為瀏覽器和下游 CDN 標記 Cache-Control: private, no-store。
錯誤回應
EmDash 會讓每個 404 都不進入路由快取,因此之後發佈一個原本缺失的項目時,它會立即顯示出來。其他錯誤回應,例如快取路由上轉譯出的 500,會繼承規則的 TTL。請使用 Astro.cache.set(false) 讓它們退出快取。
中介軟體中的重新導向
在中介軟體中透過重新導向直接回傳的情況(例如主機名稱正規化),必須使用 context.cache?.set(false) 選擇退出。否則該重新導向會依路由規則的 TTL 被快取,而在不依主機名稱區分快取項目的 CDN 提供者上,它還可能被提供給針對正規主機名稱的請求,從而立即造成重新導向迴圈。請使用選擇性鏈結:context.cache 在某些回應上是 undefined,例如與任何路由都不相符的 URL 所回傳的 404 頁面。
編輯者與快取頁面
路由快取為每個 URL 儲存一個共用的變體,其快取鍵會忽略 cookie,因此快取命中永遠不會反映編輯者的工作階段。使用預設的伺服器端轉譯工具列時,瀏覽公開網站的編輯者通常會得到快取的匿名變體(不含工具列),emdash-edit-mode cookie 對已快取的 URL 不起作用。為已登入編輯者轉譯的頁面絕不會存入共用快取;由下一位匿名訪客來填充它。
對於使用路由快取的網站,請設定 toolbar: "client"。隨後會有一個小指令碼在快取頁面上向已登入編輯者顯示編輯按鈕,而編輯檢視從始終即時轉譯的 ?_edit URL 載入。無論哪種模式,預覽連結都會繞過快取。
提供者差異
使用 memoryCache() 的 Node.js
- 每個伺服器程序都有自己的快取,失效操作只會清除處理該寫入的程序。如果你執行多個程序或執行個體,其他程序會繼續提供它們快取的頁面,直到
maxAge到期。 - 重新啟動會清空快取。
memoryCache()只儲存帶有maxAge的回應,因此沒有規則的路由永遠不會被快取,即使頁面使用快取提示呼叫了Astro.cache.set()。- 在 Node 上,排程發佈不會使路由快取失效。列出了排程發佈項目的頁面會在其快取副本過期後才顯示該項目,因此在列出排程內容的路由上請保持較短的
maxAge。
使用 cacheCloudflare() 的 Cloudflare
- 該提供者會寫入
Cloudflare-CDN-Cache-Control和Cache-Tag回應標頭。Workers Cache 會使用這兩個標頭;用戶端永遠看不到它們,你的頁面為瀏覽器設定的任何Cache-Control標頭也不受影響。 - 設定了快取提供者時,
@astrojs/cloudflare轉接器會自動啟用 Workers Cache:建置會把"cache": { "enabled": true }寫入產生的 Wrangler 設定,wrangler deploy會讀取該設定。 - 沒有規則的路由會被標記為
no-store,但前提是在它轉譯期間沒有任何程式碼呼叫Astro.cache.set()。不帶maxAge的快取提示(例如來自getSiteSettingsWithCacheHint()和getMenuWithCacheHint()的提示)會產生不帶有效期的Cloudflare-CDN-Cache-Control: public,因此 Workers Cache 會回退到它的預設快取時長(200回應為 2 小時)。如果你的版面配置設定了這些提示,那麼每個使用該版面配置的頁面,無論有沒有規則,都會變得可快取。 - 預設情況下,Worker 版本是快取鍵的一部分,因此每次部署都從空快取開始,部署後的最初幾次請求會在 Worker 中轉譯。若要讓項目在多次部署之間保留,請在 Wrangler 設定中設定
"cache": { "enabled": true, "cross_version_cache": true }(參見跨版本快取)。這樣一來,範本變更只會在受影響的項目過期或被失效之後才會顯示。 - 排程發佈會由 Cron Trigger 使其所發佈項目的標籤失效。
清除速率限制
Cloudflare 提供者透過清除 Workers Cache 來實現失效。無論你的帳戶使用哪種方案,Workers Cache 的清除一律採用 Cloudflare 的免費方案清除限制:每個帳戶每分鐘 5 個請求,突發上限為 25(參見清除速率限制)。大多數內容寫入各消耗一次清除請求:改變線上內容的儲存(包括在直接儲存到線上項目的集合中的自動儲存)、發佈、取消發佈、刪除、還原、排程,以及為項目指派分類法術語,外加每一次設定、選單、分類法或小工具的編輯。一次重新導向寫入會依來源路徑變體各消耗一次清除請求,因此當重新導向的來源發生變化時,最多會消耗四次。
被限速的清除會在管理後台中靜默失敗,受影響的頁面會保留其快取版本,直到 maxAge 到期。在大量編輯期間,或有多名編輯者同時工作時,請選擇一個你能接受的 maxAge,把它當作內容過期的最壞情況。
瀏覽器快取是獨立的
路由快取之所以能夠使用較長的 TTL,是因為 EmDash 會使它失效;而瀏覽器快取不會被失效。EmDash 的提示只提供清除標籤,不會自動提供 Last-Modified 驗證器,也不會提供 Astro 隱含的 Cache-Control: no-cache 標頭。單一項目的修改時間並不涵蓋設定、相關清單或已刪除的項目,因此把它用作頁面驗證器,可能會透過條件式的 304 回應保留過期的內文。
請使用 Astro.response.headers.set("Cache-Control", ...) 設定瀏覽器快取:使用 no-cache 讓每次造訪都重新檢查,或者使用較短的有效期(以分鐘計,而不是以天計)。若要啟用條件式請求,請提供你自己的 lastModified 提示,並確保回應的任何相依項目發生變化時,其時間戳記都會隨之前進。
對於項目所顯示的修改日期,請使用 entry.data.updatedAt,而不是 EmDash 不會自動填入的 cacheHint.lastModified。