題幹與適用場景
產品希望在 SPA 路由切換和圖片詳情展開時加入 View Transition API。請說明如何實作,並處理舊瀏覽器、prefers-reduced-motion、非同步資料、焦點管理、動畫失敗和效能回退。
這道題考察 API 邊界和漸進增強,而不是只展示一段動畫程式碼。MDN 將 document.startViewTransition() 定義為同文件視圖更新入口,並說明回呼完成後才開始轉場;跨文件轉場還需要同源頁面和 CSS @view-transition。動畫必須服務狀態變化,不能阻塞可用性。
面試官考察點
第一項是能否區分同文件 SPA、元素範圍和跨文件轉場。第二項是能否處理不支援 API、回呼拒絕、使用者減少動態效果和頁面離開等情況。第三項是能否保證焦點、語意 DOM、捲動和真實互動不被快照動畫掩蓋。
回答前需要釐清的問題
- 轉場發生在哪個邊界? 是同文件 DOM 更新、單個元素,還是跨文件導覽?
- 更新是否包含非同步資料? 轉場前要等待什麼,最長等待多久?
- 舊瀏覽器的可接受體驗是什麼? 無動畫也必須完成同一狀態更新。
- 哪些使用者應減少或關閉動畫?
prefers-reduced-motion和應用設定如何共同決定? - 頁面狀態如何恢復? 焦點、捲動位置、表單輸入和返回導覽是否保留?
- 效能預算是什麼? 截圖範圍、動畫時長、低階裝置和並行轉場如何限制?
30 秒回答框架
「我先把狀態更新和動畫分開:支援 View Transition 時,用 startViewTransition 包住同步 DOM 更新;不支援時直接更新。非同步資料先由路由或元件準備,回呼只提交已準備好的狀態,並監聽 ready、finished 和 skipTransition。CSS 用命名視圖控制範圍,prefers-reduced-motion 下縮短或關閉動畫。更新後恢復焦點與捲動,動畫失敗不能阻塞互動。最後用長任務、截圖數量、完成時間和回退率驗證低階裝置表現。」
分步驟深入解答
第一步:先定義狀態更新和動畫目標
明確動畫連接的是哪兩個狀態,例如列表卡片到詳情頁、篩選結果到新列表,而不是讓整頁無差別淡入。狀態更新必須在沒有動畫時也正確完成,動畫只是增強層。為每種導覽類型定義允許的轉場名稱和回退行為。
第二步:偵測能力並保留同步回退
呼叫前檢查 document.startViewTransition。不支援時直接執行同一個更新函式,不要複製兩套業務邏輯。新瀏覽器支援不代表所有舊裝置都支援,MDN 的相容性資訊仍要求準備降級路徑。
第三步:控制回呼和非同步資料邊界
updateCallback 在舊視圖快照後執行,返回的 Promise 完成後才進入下一幀。資料取得應盡量在呼叫前完成;若必須在回呼中等待,要設定逾時並允許跳過動畫。回呼拒絕時保持業務錯誤處理,不讓半更新狀態進入頁面。
const update = () => renderState(nextState);
if (!document.startViewTransition || reduceMotion) {
update();
} else {
const transition = document.startViewTransition(update);
transition.finished.catch(() => {
// 動畫失敗不回滾已完成的狀態更新
});
}第四步:用命名視圖限制截圖範圍
只給需要共享位置的元素設定唯一 view-transition-name,避免為整棵大 DOM 樹建立快照。列表中重複元件不能共享同一個名稱,否則會產生歧義;動態列表要在更新前清理舊名稱,並在更新後為新元素設定穩定名稱。
第五步:實作減少動態效果和無障礙行為
監聽 prefers-reduced-motion: reduce,把動畫時長設為零或極短,並保留狀態變化。動畫期間不要隱藏鍵盤焦點或依賴顏色變化傳達結果;更新完成後把焦點放到新視圖的標題或等價位置。web.dev 特別提醒全螢幕動畫可能讓前庭障礙使用者不適。
第六步:處理 SPA、元素和跨文件差異
SPA 用 document.startViewTransition 包住 DOM 更新;元素範圍轉場只影響呼叫元素及其後代;跨文件導覽要滿足同源,並在兩端 CSS 中啟用 @view-transition。不要把同文件的 JS 回呼假設直接帶到跨文件生命週期。
第七步:設計跳過、並行和失敗路徑
使用者快速連續點擊時,先取消或合併舊導覽,避免多個轉場同時爭用同一節點。透過 skipTransition() 或逾時快速結束卡住的動畫。業務狀態已更新時,動畫失敗只影響視覺效果,不應觸發重複提交、重複請求或錯誤回復。
第八步:驗證效能和真實使用者體驗
監控轉場啟動到完成時間、跳過比例、長任務、CLS、輸入延遲和低階裝置錯誤。用大列表、慢網路、非同步拒絕、快速返回、螢幕閱讀器和減少動態效果做演練。確認截圖和動畫不會讓真實互動等待更久。
設計取捨與邊界
取捨一:整頁轉場還是局部轉場
整頁轉場實作簡單,但截圖和動畫成本更高,也更容易干擾焦點和捲動。局部轉場需要穩定命名和更精確的元件邊界,適合高頻互動與大頁面。
取捨二:等待資料還是立即轉場
等待資料可以避免舊內容與新內容錯位,但會延長回應時間。優先預取資料,無法預取時給出明確的載入狀態;轉場不是取代載入回饋的理由。
取捨三:自訂動畫還是瀏覽器預設
預設動畫更安全、更易維護。只有在明確的導覽語意和驗證預算下才覆蓋偽元素動畫,並為減少動態效果提供同等功能的靜態狀態。
失敗演練與演進計畫
演練一:瀏覽器不支援或回呼拒絕
在不支援 API 的瀏覽器直接更新,模擬 Promise 拒絕並確認頁面狀態仍正確。錯誤只記錄可診斷資訊,不顯示實作細節,也不阻塞使用者繼續操作。
演練二:快速導覽與非同步競態
連續點擊兩個連結,故意讓第一個請求更慢。驗證舊轉場不會覆蓋新狀態,焦點最終落在目前頁面,且不會發送重複副作用請求。
演練三:減少動態效果和低階裝置
開啟系統 reduce motion,在 CPU 受限裝置執行大列表切換。確認動畫被關閉或縮短,輸入延遲和版面穩定性仍在預算內。
常見誤區與追問
誤區一:沒有回退路徑
API 不可用時仍必須完成同一 DOM 或路由更新。漸進增強不能把業務狀態綁定在動畫成功上。
誤區二:在回呼中無限等待網路
快照已建立後長時間等待會讓使用者看到凍結的舊頁面。提前準備資料,或設定逾時並跳過動畫。
誤區三:給大量元素設定同名視圖
重複名稱會造成匹配歧義和額外截圖。只命名真正需要共享位置的單個元素,並保證名稱穩定唯一。
誤區四:忽略焦點和捲動
視覺轉場結束不代表鍵盤使用者回到了正確位置。顯式恢復焦點、捲動和語意標題。
誤區五:把 reduce motion 當成刪除功能
使用者只要求減少動態效果,不是關閉狀態變化。保持同樣的資訊和互動,只改變動畫表現。
誤區六:動畫失敗就回復業務狀態
finished 失敗通常只說明視覺階段失敗。不要重複提交或回復已成功的狀態更新。