Linear 效能架構:技術解析
Linear 透過翻轉傳統的客戶端-伺服器關係來實現其感官上的速度。UI 不再等待伺服器對每個動作做出回應,而是將瀏覽器視為主要資料庫,採用「本地優先」(local-first)架構,將變更立即應用於本地儲存,並與伺服器進行非同步同步。
本地優先資料架構
Linear 效能的核心在於將網路請求從使用者互動的關鍵路徑中消除。透過將應用程式狀態儲存在 IndexedDB 中,並將其注水(hydrating)到記憶體中的 MobX 可觀察圖(observable graph),UI 可以直接在本地讀寫資料。
基於瀏覽器的資料庫
在傳統的 CRUD 應用程式中,使用者動作會觸發 HTTP 請求、伺服器查詢,以及隨後的 UI 重繪,這通常會導致載入動畫(loading spinners)的出現。Linear 以本地優先的方法取代了這個循環:
- 本地變更 (Local Mutation):當使用者更新一個 issue 時,變更會立即應用於記憶體中的資料儲存。
- 非同步同步 (Asynchronous Sync):變更會被寫入 IndexedDB 中的持久化交易隊列(durable transaction queue),然後在背景進行批次處理並推送到伺服器。
- 伺服器廣播 (Server Broadcast):伺服器確認變更後,透過 WebSockets 向其他客戶端廣播增量更新(deltas)。
使用 MobX 進行細粒度重繪
為了防止 UI 在大型更新期間出現延遲,Linear 使用 MobX 來確保只有依賴於已變更欄位的特定組件會被重新渲染。由於每個模型上的每個屬性都是其自身的「可觀察對象」(observable),單個 issue 欄位的變更會觸發「單一單元格」(one cell)的重繪,而非整個列表的重繪。這使得應用程式即使在多個使用者同時編輯工作區時,也能保持流暢。
優化首次載入
Linear 利用激進的程式碼分割(code splitting)、預載入(preloading)以及內聯應用程式外殼(inlined app shell)的組合,讓初始頁面載入感覺像是瞬間完成的。
建置時優化
Linear 持續演進其建置管線(從 Parcel 遷移到 Rollup,接著是 Vite,現在是 Rolldown),以減少交付給客戶端的 JavaScript 和 CSS 數量。關鍵策略包括:
- 捨棄舊版支援:針對現代瀏覽器,以消除 polyfills 和 ES5 轉譯。
- 激進的程式碼分割:將應用程式拆分為數百個路由層級的區塊(chunks)。每個大於約 3KB 的 npm package 都會被拆分為獨立的區塊,以確保更新單一依賴項時不會使整個 vendor cache 失效。
平行載入與預快取
為了避免瀏覽器依序抓取腳本的「瀑布流」(waterfall)效應,Linear 在 HTML head 中使用了 <link rel="modulepreload">。這讓瀏覽器能在進入腳本(entry script)執行之前,就平行地抓取關鍵區塊。
此外,一個 service worker 會在首次載入後於背景預快取(precaches)約 1,200 個帶有雜湊值的資產(路由區塊、圖示和字體)。這確保了隨後的導覽可以完全跳過網路,並讓應用程式在離線狀態下仍能功能完備。
內聯應用程式外殼與立即渲染
Linear 透過將繪製載入狀態所需的關鍵樣式直接內聯在 <head> 中,消除了 CSS 的初始網路請求。它還使用內聯啟動腳本來讀取 localStorage 中的使用者偏好(主題、側邊欄寬度)和身分驗證狀態。
至關重要的是,Linear 採用了「先渲染,後驗證」的策略。如果 localStorage.ApplicationStore 存在,應用程式會假設使用者已登入,並立即從 IndexedDB 渲染完整的體驗,同時在背景透過伺服器驗證 session token。如果 session 為過期狀態,使用者僅會在第一個請求失敗後才被重新導向至登入頁面。
設計與動畫原理
工程速度透過設計選擇來補充,這些設計減少了完成任務所需的物理與認知負擔。
鍵盤優先導覽
Linear 整合了全面的快捷鍵系統和全域指令面板 (⌘ K)。指令面板反應極快,因為它搜尋的是本地的 MobX 物件池,而非發送伺服器請求,這使得導覽與動作執行幾乎是瞬間完成的。
GPU 加速動畫
為了維持高幀率,Linear strictly 限制動畫僅限於合成屬性(composited properties)——主要是 transform 和 opacity——這些屬性由 GPU 處理,且不會觸發佈局重新計算(layout re-computations)。
Linear 也採用了非對稱定時(asymmetric timing):元素在被喚起時會立即出現,但在消失時會經過約 150ms 的淡出效果。這讓介面感覺反應靈敏,同時提供必要的空間上下文。
技術權衡與社群觀點
雖然本地優先架構提供了顯著的速度,但也為資料一致性與衝突解決引入了 complexities 複雜度。
同步挑戰
社群討論指出,建立一個強大的同步引擎並非易事。潛在問題包括:
- 衝突解決:處理兩個離線使用者同時編輯同一個 issue 或一個使用者刪除 issue 而另一個使用者正在編輯它時的情境。
- 一致性:存在「同步延遲」的風險,使用者可能無法確定自己的更新是否已傳送到團隊。
- 狀態管理:在使用者離線與重新連線之間,管理 schema drift(架構變動)與業務邏輯變更的難度。
使用者體驗差異
部分使用者回報,當沒有視覺指示器顯示資料正在背景同步時,UI 的樂觀更新(optimistic nature)可能會令人困惑。其他人則指出,雖然應用程式比傳統工具如 Jira 比起顯著快得多,它仍可能遇到 CPU 尖峰或「同步中」鎖定導致互動受阻的情境。