前端前線 2026 年 9 月 24 日

2026-09-24 — 雙軌虛擬化改寫 Copilot 巨型 PR 渲染

primary=https://github.blog/engineering/user-experience/rendering-huge-pull-requests-in-the-github-copilot-app/

雙軌虛擬化改寫 Copilot 巨型 PR 渲染

GitHub Blog (Engineering) · 2026-09-23

GitHub Copilot App 把「程式碼列」與「留言區塊」拆成兩套獨立的量測系統,取代原本每個留言掛一個 ResizeObserver、和程式碼共用同一套虛擬化清單的做法。這篇工程文章由 Principal Design Engineer Alberto Gimeno 發表於 GitHub Blog(Engineering),用一個 2,200 個檔案、超過 100 萬行變更、400 多則行內留言的真實開源 PR 驗證新架構。

背景:原本的問題是什麼

過去的 PR 檢視畫面用單一虛擬化清單渲染整個 diff,把程式碼行與留言都當成同一種「列」處理位置。這在小型 PR 上沒問題,留言一多就出事:留言的高度要等內容真正渲染出來才知道,虛擬化清單卻得在渲染前先決定每一列的座標。團隊試過最直覺的解法——替每個留言區塊掛一個 ResizeObserver,量到新高度就回寫進版面。

這個解法有四個實際問題:量測會互相觸發形成回饋迴路;觀察成本隨掛載區塊數量線性上升;留言在捲動途中變形造成畫面跳動;舊估計值失準時,留言下方會留下空白間隙。問題只在留言多、PR 大時才會浮現,小型 PR 測不出來。

核心改動:實作細節

新架構把兩種內容徹底分開量測。程式碼列的位置是確定性的:後端直接把 diff 結構化串流回來,前端用型別陣列一次算好所有列的偏移量與高度,之後不再重算,也不為每一列建立 React 元件,改用命令式、可回收的渲染器直接操作 DOM。留言、草稿與回覆輸入框改用獨立索引,各自以「檔案、行號、邊」當身分鍵,搭配內容指紋判斷測量是否仍然有效,並用寬度分桶避免視窗微調就整批失效。

量測時機也重新設計:只在捲動停下來後才排程,絕不在捲動途中量測;只處理視窗上下約 2400px 範圍內的區塊,讓成本維持在 O(viewport) 而非隨 PR 大小成長;已掛載區塊直接拿真實渲染高度當結果;尚未進視窗的區塊最多搶先渲染一次。ResizeObserver 並未被丟棄,而是只用在使用者主動觸發的變化(展開詳情、開啟留言輸入框),並搭配同步量測與修正,避免舊迴圈重演。

項目舊做法新做法
留言測量時機內容一變就即時量測捲動停止後才批次量測
量測範圍所有已掛載區塊僅視窗附近約 2400px
程式碼列渲染與留言共用同一套虛擬化清單獨立 typed-array 幾何,免逐列 React 元件

影響範圍:對誰有影響、該做什麼

這個改動不只影響 GitHub Copilot App,任何要在瀏覽器渲染大型 diff 的產品都會遇到同樣的邊界問題。負責 code review 工具或內部 diff viewer 的前端工程師,若現有實作也是「一個留言一個 ResizeObserver」,可以預期留言數與 PR 行數同時變大時會踩到相同的回饋迴路與捲動跳動。建置任何虛擬化清單元件的團隊,值得檢查自己是否把確定性高度的列與動態高度的區塊混在同一套量測邏輯裡。

  • 檢查現有 diff viewer 是否對留言區塊逐一掛 ResizeObserver,評估改成捲動停止後批次量測。
  • 評估是否可把「視窗附近才量測」的範圍限制,套用到自己的長列表或留言系統。
  • 負責可觀測性的工程師可參考文中列出的健康訊號:掛載列數與留言區塊數、每禎量測提交次數、捲動修正幅度、observer 生命週期是否洩漏。

原始來源:GitHub Blog: Rendering huge pull requests in the GitHub Copilot app


End of article
0
Would love your thoughts, please comment.x
()
x