主題: Web platform
View Transitions 不會把 MPA 變 SPA,但會讓它更順
View Transition API 已能處理同文件狀態與跨頁導覽。這篇整理最小用法、瀏覽器退路、shared element 命名與 reduced motion。
動態迷因(展開/收合)
以前想讓文章列表的縮圖平順移到內頁 hero,常見做法是先把網站變成 SPA,再接 router transition、狀態管理與一段專門等動畫結束的 JavaScript。
現在跨頁效果可以從兩行 CSS 開始:
@view-transition {
navigation: auto;
}
難怪最近的 daily.dev View Transitions recipes 與 Reddit 的現代 Web API 討論 都很熱鬧。這個 API 終於從有趣 demo,走到值得放進一般產品的階段。
不過先拆掉一個誤會:View Transitions 只負責視覺狀態的交接,不負責 routing、資料取得、cache、optimistic update 或離線狀態。 它能讓 MPA 看起來更連續,卻不會把 MPA 變成 SPA。
這反而是它最好的地方。若網站本來不需要 client router,就不用為了一段轉場把整套架構搬進瀏覽器。
其實是兩種轉場,共用同一套概念
MDN 的 View Transition API涵蓋兩條路徑:
| 情境 | 觸發方式 | 適合工作 |
|---|---|---|
| Same-document | document.startViewTransition(update) |
排序、分頁、切換月份、SPA route |
| Cross-document | @view-transition { navigation: auto; } |
同源 MPA 的頁面導覽 |
Same-document 轉場會先擷取舊狀態,執行 callback 裡的 DOM 更新,再讓舊畫面淡出、新畫面淡入。這組核心功能在 Firefox 144 上線後,已於 2025 年 10 月成為 Baseline Newly available。
Cross-document 不需要 JavaScript。瀏覽器在舊文件與新文件之間處理 snapshot 與 navigation,但有三個前提:
- 兩頁必須同源。
- 起點與終點頁面都要用
@view-transitionopt in。 - 導覽不能經過 cross-origin redirect。
這些條件來自 CSS View Transitions Level 2 的跨文件模型。登入服務、第三方付款或外部連結通常不符合,就讓它們維持正常導覽。
最安全的 MPA 起點:只在允許動態效果時 opt in
@view-transition 目前在 MDN 仍標示為 Limited availability。好消息是,它很適合 progressive enhancement:不支援的瀏覽器忽略規則,連結照常開啟,新頁面照常載入。
我會從更保守的版本開始:
@media (prefers-reduced-motion: no-preference) {
@view-transition {
navigation: auto;
}
}
沒有動畫 library,沒有 feature-detection script,也沒有 fallback router。支援且未要求減少動態效果的瀏覽器得到預設 cross-fade;其他使用者得到原本就可靠的頁面導覽。
這個失敗模式很乾淨。如果頁面載入太慢,Chrome 甚至會在等待超過約四秒後跳過轉場,而不是讓 snapshot 永遠蓋在畫面上。轉場是加分,不該成為進入新頁面的前置條件。
Shared element 的重點不是動畫,而是 identity
整頁淡入淡出只需要 opt in。要讓列表縮圖移到文章 hero,兩邊元素必須共享同一個 view-transition-name:
/* 列表頁與文章頁各自只能有一個符合的元素 */
[data-transition-cover="42"] {
view-transition-name: cover-42;
}
列表頁的第 42 張縮圖與內頁 hero 都使用 cover-42,瀏覽器便能把舊、新 snapshot 配成一組。名字代表「這是同一個視覺物件」,不是「請套用某個動畫」。
這裡最容易踩兩個坑:
- 同一個畫面不能有兩個可見元素使用相同名稱。列表中每張卡片都寫
view-transition-name: cover,不會自動猜出點了哪張。 view-transition-name: match-element適合同文件內替大量元素自動命名,但不同文件中的 DOM 元素沒有相同 identity,不能靠它完成跨頁 shared element。
所以真正需要的通常不是 animation helper,而是一個穩定 ID。由 template 用文章 slug 或資料 ID 產生 cover-<id> 已經足夠;不必建立動畫 registry。
SPA 也不必把更新邏輯交給 API
Same-document 的最低風險寫法,是先保留一個不含動畫也能正確執行的更新函式:
function updateResults() {
results.replaceChildren(...nextItems);
}
if (document.startViewTransition) {
document.startViewTransition(updateResults);
} else {
updateResults();
}
Callback 不是讓 View Transition 接管應用程式狀態。它只是告訴瀏覽器:「舊狀態已經可以截圖,現在執行真正的 DOM 更新。」
這個界線值得守住。資料取得應先完成,錯誤處理仍由原本流程負責;動畫失敗時,資料與 DOM 更新仍必須成功。若把 fetch、router side effects 與動畫時間全部塞進 callback,原本簡單的視覺增強很快又會變成新的控制中心。
它不能替代 Router 的事
View Transitions 可以讓 server-rendered 頁面在 navigation 時交接得更自然,但下面需求仍是 architecture,不是 animation:
- App shell 必須長期保留
- 切頁後要保存複雜 client state
- 需要 optimistic UI 或離線操作
- 只更新部分資料,不重新取得完整文件
- Route loader、prefetch 與 cache 是產品效能核心
有這些需求,client router 仍可能是正確選擇。沒有這些需求,只想避免頁面切換太突兀,就先用平台原生的跨文件轉場。
我會這樣選:
| 需求 | 做法 |
|---|---|
| 月曆前後月份、列表重新排序 | Same-document transition |
| 文章列表進入文章內頁 | Cross-document transition |
| Auth、付款、外部網站 | 普通 navigation |
| Optimistic dashboard、離線 app | Router/SPA architecture |
| Button hover、展開一個區塊 | 普通 CSS transition |
最後一列很重要。不是每個位移都需要 snapshot 與 pseudo-element tree;元素自己還活在同一個位置時,CSS transition 通常更直接。
動態迷因(展開/收合)
動畫要解釋方向,不要只證明技術存在
View Transition 預設是 cross-fade,已經比「每頁都從右邊飛進來」安全。真正值得自訂的動畫,通常能幫使用者理解空間或因果:
- 點縮圖後,圖片成為內頁 hero
- 月曆往下一個月時向左移,回上一個月時向右移
- 排序後,項目移到新位置而不是瞬間消失再出現
相反地,整頁旋轉、縮放、3D flip 雖然適合 demo,未必適合每天操作的產品。動畫時間越長,越像在收取使用者的注意力稅。
也不要把 prefers-reduced-motion 理解成「動畫播放快一點」。MDN 的 reduced motion 指南是用較溫和效果取代可能引發前庭不適的縮放動態。最簡單的產品預設,是只在 no-preference 下啟用跨頁轉場;如果保留 reduced mode,就用短 cross-fade,別用大幅移動與縮放。
上線前的五個檢查
- 關掉支援再測一次。 所有操作仍要正常完成。
- 啟用系統 Reduce Motion。 頁面不能偷偷保留大幅 slide 或 scale。
- 測 Back/Forward。 BFCache 恢復時,暫時加入的 transition name 要清掉。
- 測慢頁面與失敗回應。 動畫不得遮住錯誤或阻擋 navigation。
- 測 responsive breakpoint。 同名元素在任何寬度都只能出現一個。
這五項都通過後,再討論 easing 與 duration。因為 View Transitions 的最佳 fallback 不是另一套動畫,而是本來就能用的網站。
結論:把轉場當 CSS,不要當架構
View Transition API 最值得注意的進展,不是它能做多少華麗效果,而是正常網站不必先變成 SPA 才能擁有連續的頁面感。
Same-document 核心功能已跨主要瀏覽器;cross-document 即使尚未完整 Baseline,也能在不支援時安靜退回普通導覽。這正是適合現在採用的 progressive enhancement。
我的預設很簡單:先讓 navigation 與 DOM update 在零動畫時完全正確,再用 View Transitions 補上能解釋位置、方向或因果的動態。
若一段轉場開始要求自製 router、global animation store 或複雜 lifecycle manager,問題多半不是 API 不夠強,而是視覺增強已經越權成為架構。
外部參考連結
- MDN:View Transition API
- MDN:Using the View Transition API
- MDN:
@view-transition - MDN:
prefers-reduced-motion - W3C:CSS View Transitions Module Level 1
- W3C:CSS View Transitions Module Level 2
- web.dev:Same-document view transitions are Baseline Newly available
- WebKit:Two lines of Cross-Document View Transitions
- daily.dev:7 View Transitions Recipes to Try
- Reddit:What’s new in web development that you use regularly?