具代表性的面試主題

前端面試:如何安全地使用 View Transition API?

前端困難
OfferCC 編輯團隊發佈 更新

題幹

產品希望在 SPA 路由切換和圖片詳情展開時加入 View Transition API。請說明如何實作,並處理舊瀏覽器、prefers-reduced-motion、非同步資料、焦點管理、動畫失敗和效能回退。

題幹與適用場景

產品希望在 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 完成後才進入下一幀。資料取得應盡量在呼叫前完成;若必須在回呼中等待,要設定逾時並允許跳過動畫。回呼拒絕時保持業務錯誤處理,不讓半更新狀態進入頁面。

js
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 失敗通常只說明視覺階段失敗。不要重複提交或回復已成功的狀態更新。

公開來源

同類題目