題幹與適用情境
你負責長列表、文章聚合頁或後台工作台。DOM 裡有數百個內容區塊,首屏只看到少量卡片,瀏覽器卻仍要為螢幕外子樹做樣式、版面與繪製。題目要求使用 CSS 的渲染隔離能力降低主執行緒工作,同時保留尋找、焦點和螢幕閱讀器可達性。
預設假設:區塊可以按卡片或章節切分,內容高度差異有限但並非完全固定;瀏覽器支援 content-visibility: auto。若目標瀏覽器不支援,頁面仍必須正常顯示。
面試官考察重點
- 能否區分網路、腳本與渲染瓶頸,而不是看到慢就加入延遲載入。
- 能否解釋
auto帶來的 layout、style、paint containment,以及螢幕外的 size containment。 - 能否發現尺寸佔位錯誤會造成捲軸跳動和 CLS 風險。
- 能否把效能收益與焦點、尋找、螢幕閱讀器和 DOM 讀取一起驗證。
普通回答只給一行 CSS。強回答會說明適用邊界、佔位策略、強制版面讀取的代價和回退方案。
回答前需要釐清的問題
- 慢發生在首屏、捲動、篩選後更新,還是點擊後互動?不同階段決定要測渲染、腳本還是網路。
- 卡片高度是否穩定?差異越大,越需要真實測量或使用
contain-intrinsic-size: auto記住已渲染尺寸。 - 螢幕外內容是否必須支援瀏覽器尋找、鍵盤焦點和輔助技術?若必須,不能把
hidden當成display: none的替代品。 - 是否有程式在每次捲動或狀態更新時讀取
offsetHeight、getBoundingClientRect()等版面屬性?這會把被略過的渲染工作重新拉回來。
30 秒回答框架
「我先用 Performance 面板確認瓶頸確實是大量螢幕外子樹的渲染。把內容切成獨立區塊,對非首屏區塊使用 content-visibility: auto,並提供合理的 contain-intrinsic-size,避免 size containment 把高度當成零。接著檢查焦點、尋找和輔助技術行為,稽核會強制版面計算的 DOM 讀取。最後用實驗組和對照組比較首屏渲染、捲動、INP、CLS,以及不支援該屬性時的回退表現。」
分步驟深入解答
1. 先切分渲染邊界
把長頁面拆成互不依賴的章節或卡片。邊界內的版面變化不應影響頁面其他區域,否則 containment 會隱藏真實依賴,產生錯誤版面。
.story {
content-visibility: auto;
contain-intrinsic-size: auto 720px;
}auto 讓瀏覽器在區塊遠離視窗時略過後代的部分樣式、版面和繪製;接近視窗時再按需渲染。它仍保留 DOM,不等同刪除節點。
2. 處理 size containment 的佔位
螢幕外區塊會暫時依自身盒子計算尺寸。若沒有明確高度或 intrinsic size,區塊可能被當成很矮的空盒,捲軸長度會變化,使用者捲動位置也可能跳動。
contain-intrinsic-size: auto 720px 提供首次估計;區塊渲染後,瀏覽器可以記住真實尺寸。估計應來自真實樣本,不要為了好看隨便寫數字。高度差異很大時,按內容類型分組或在資料層提供尺寸提示。
3. 區分 auto、hidden 與 display none
auto:螢幕外略過渲染,進入視窗時恢復;內容仍在 DOM 和可及性樹中。hidden:始終略過渲染,但保留渲染狀態,適合暫時停用的視圖;不應當成通用的可及性隱藏工具。display: none:移除版面和渲染狀態,再顯示時需要重新建立。
如果內容不應被輔助技術看到,應使用語意正確的 aria-hidden 或真正移除 DOM,並確認焦點不會落入隱藏區域。
4. 稽核會破壞收益的讀取
在 content-visibility 區塊上頻繁讀取版面屬性,會迫使瀏覽器提前計算被略過的子樹。把測量放在確實需要的時機,批次讀取後再批次寫入,避免讀寫交錯造成強制同步版面。
requestAnimationFrame(() => {
const height = card.getBoundingClientRect().height;
card.style.setProperty('--measured-height', `${height}px`);
});這段只示意時機;實際專案要透過 Performance 錄製確認讀取是否導致長任務,而不是看到 API 名稱就全部刪除。
5. 用指標驗證而非只看 Lighthouse
實驗至少包含:
- 首屏渲染和總渲染時間:確認螢幕外工作真的減少。
- INP 或點擊後的長任務:確認互動主執行緒得到空檔。
- CLS 和捲動位置:確認尺寸佔位沒有製造跳動。
- 鍵盤 Tab、瀏覽器尋找、螢幕閱讀器:確認
auto的可及性符合需求。 - 不支援該屬性的瀏覽器:確認預設
visible仍能完整顯示。
web.dev 的範例把分塊內容從 232ms 降到 30ms,但這是特定頁面的實驗結果,不能當作所有網站的承諾;結論必須來自自己的對照測量。
高品質示範回答
我會先把問題定義為「螢幕外渲染工作過多」,再用 Performance 面板確認主執行緒時間是否主要花在樣式、版面和繪製。確認後,我把頁面拆成相互獨立的區塊,對區塊設定 content-visibility: auto,並以歷史樣本估計 contain-intrinsic-size。這樣瀏覽器可以略過螢幕外後代的渲染,同時保留 DOM 和輔助技術可達性。
我不會只看首屏時間。我要檢查捲軸是否跳動、焦點和尋找是否正常,並搜尋 getBoundingClientRect、offsetHeight 等讀取是否在捲動或狀態更新中觸發強制版面。最後做對照實驗,比較首屏渲染、捲動長任務、INP、CLS 和真實使用者資料。若卡片高度差異太大或區塊有跨邊界版面依賴,我會先調整切分和尺寸策略;若瀏覽器不支援該屬性,預設 visible 作為回退,功能優先於最佳化。
常見錯誤
- 錯誤表現 → 全頁面直接加
content-visibility: auto→ 失敗原因 → 邊界不清會隱藏版面依賴,收益也無法歸因 → 修正方法 → 先按獨立內容塊切分並錄製基線。 - 錯誤表現 → 不設定 intrinsic size → 失敗原因 → size containment 可能把區塊高度估得過小,捲軸和 CLS 變差 → 修正方法 → 用樣本估計尺寸,並觀察真實捲動位置。
- 錯誤表現 → 用
hidden代替aria-hidden→ 失敗原因 → 視覺隱藏與輔助技術語意不同 → 修正方法 → 依產品語意選擇移除 DOM、aria-hidden或保留可及性內容。 - 錯誤表現 → 看到效能 API 就全部刪除 → 失敗原因 → 某些測量確實必要,盲刪會破壞互動 → 修正方法 → 以 Performance 錄製確認哪些讀取觸發強制版面。
追問及應對
如果卡片高度差異達到五倍,你還會用同一個 intrinsic size 嗎?
不會。統一估計會讓部分卡片嚴重偏小或偏大。我會按內容類型分桶,優先使用已渲染尺寸記憶;必要時由伺服器回傳尺寸提示,並用 CLS 與捲動誤差驗證分桶是否值得增加複雜度。
如果產品要求螢幕外影片完全停止解碼,content-visibility 足夠嗎?
不夠。它主要控制渲染工作,不能取代媒體資源生命週期管理。我會在可見性變化時暫停或恢復影片,並確保暫停邏輯不會頻繁讀取被略過子樹的版面;媒體策略和 CSS 最佳化分開測量。
如果舊瀏覽器不支援該屬性,如何發布?
不依賴它提供功能。預設 content-visibility 是 visible,因此頁面應保持完整可用;用漸進增強和相容性監控確認舊瀏覽器的效能是否需要另一個方案,例如分頁或虛擬列表。
如何證明收益來自它,而不是減少了 DOM?
保持同一資料量和 DOM 結構,只切換 CSS 屬性做 A/B 或本地對照;同時記錄渲染耗時、長任務、INP 和 CLS。若還改變了分頁、圖片延遲載入或腳本排程,就不能把差異歸因給單一屬性。