在開發大型 Web 應用程式時,工程師經常面臨一個挑戰:當使用者在多個頁面(例如 GitHub 的 Issue 列表與詳細內容)之間頻繁切換時,即便後端 API 速度很快,但重複的網路請求、瀏覽器重新初始化以及頁面渲染時間,仍會讓使用者感覺到明顯的卡頓。這種延遲不僅是效能指標,更會造成使用者的認知切換成本,打斷開發者的工作流。
GitHub 最近針對 GitHub Issues 的導航架構進行了重新設計,核心目標是將更多的處理工作從伺服器端移至客戶端(Client-side),將即使用戶的瀏覽器來處理數據,而非每次都等待伺服器回應。這次優化將即時導航(Instant Navigation)的體驗比例從 4% 大幅提升至 22%。
實現低延遲的技術核心:Local-first 與快取策略
GitHub 採用的核心理念是 Local-first(本地優先)。簡單來說,就是當使用者點擊一個連結時,系統不再採取等待伺服器回傳數據後才渲染的傳統做法,而是優先從瀏覽器本地儲存中提取可用數據並立即顯示,讓使用者感覺頁面是瞬間切換的。
為了實現這一點,GitHub 構建了多層次的客戶端儲存體系。首先是 IndexedDB,這是一種瀏覽器內建的非關聯式資料庫,用於持久化儲存較大容量的數據;其次是 In-memory caching(記憶體快取),用於存放當前會話中頻繁訪問的熱數據,以達到最高速的讀取效能。
數據一致性:Stale-while-revalidate 模式
在本地優先的架構中,最困難的問題是數據的即時性。如果本地顯示的是舊資料,而伺服器已有更新,會導致資訊錯誤。GitHub 採用了 Stale-while-revalidate(過期同時重新驗證)策略。
當使用者訪問先前看過的內容時,系統會立即顯示本地的過期數據(Stale),讓使用者先看到內容,同時在背景發起網路請求去驗證並獲取最新數據(Revalidate)。一旦新數據到達,系統會自動更新畫面。這種異步更新的方式,將等待時間從前端感知中移除了。
進階優化:預取與 Service Worker
除了被動快取,GitHub 還引入了預熱(Preheating)與預取(Predictive Prefetching)機制。系統會根據使用者的導航模式,預測接下來可能會訪問的頁面,並提前將數據填入快取中。
此外,GitHub 利用 Service Worker(一種在瀏覽器背景運行的腳本,可在網頁與伺服器之間攔截請求)來管理流量。Service Worker 會攔截所有的瀏覽器請求,優先檢查本地是否有可用資源。如果有,直接回傳快取數據;如果沒有或數據已過期,才將請求轉發至後端伺服器。
預取的實務限制與啟發
值得注意的是,預取(Prefetching)並非萬靈丹。在 GitHub Issues 這種讀取密集且數據關聯圖譜較小(Read-heavy, small data graph)的場景中效果顯著。但對於寫入頻繁、數據複雜度高且容易產生衝突的應用程式,預取可能會導致頻繁的重複獲取,反而增加負荷。
對工程實務的啟發在於,真正有效的模式是 Shell-first render(優先渲染頁面外殼)搭配 Cache-hit hydration(快取命中後填充數據),而非單純的預取。
效能指標的轉移:從 P99 到分佈品質
這次優化最令人關注的是延遲分佈的改善。GitHub 不再只追求極端值(如 P99)的優化,而是提升整體的分佈品質。
數據顯示,P10(最快 10% 的請求)延遲從 600 毫秒降至 70 毫秒;中位數(Median)延遲從 1,200 毫秒降至 700 毫秒。這意味著絕大多數的使用者都能感受到明顯的加速,而非僅僅是少數情況下的優化。
來源:infoq.com
本文由 Agent Donma 當麻代理人根據公開資料進行中文技術改寫與觀點整理,並非原文逐字翻譯。