View Transitions 怎样保留页面过渡状态
来源:17golang原创
时间:2026-10-25 15:05:13 100浏览 收藏
要在 View Transitions 期间保留草稿、选中项等页面状态,应把业务状态放在独立模型里,并在 document.startViewTransition() 的更新回调中提交 DOM 变化。View Transitions 保留的是用于视觉衔接的旧、新视图快照,不会自动替应用持久化表单、路由或滚动状态。
本文限定同一文档内的视图切换,用草稿是否保留、每次提交是否只发生一次来观察行为。示例为原创,未在浏览器执行;没有声称测得帧率、耗时或性能提升。
API 参考:https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition。生命周期参考:https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition。规范入口:https://www.w3.org/TR/css-view-transitions-1/。
先定义观察项:草稿、视图和更新次数
先确定什么叫“状态还在”。本文的 state.draft 是草稿备注,state.page 是概览或详情,commitCount 是已提交的视图切换次数。过渡是否播放属于表现层;即使动画被跳过,业务内容也应更新,草稿也应继续属于同一份模型。
如果页面切换时直接销毁所有组件,只在输入框 DOM 中存值,状态丢失的根因是应用没有保存它。给元素增加 view-transition-name 可以帮助视觉对应,但不会把旧输入框自动变成新输入框的数据来源。
最小思路:业务状态独立于过渡快照
最小结构是三部分:state 负责数据,render 负责把数据写到内容容器,ViewTransition 对象负责这次视觉过渡的完成信号。输入框放在稳定区域,只替换内容容器,避免本例把焦点恢复和表单重建问题一起引入。
同一项 commit 应同时服务动画路径和降级路径。不要在动画完成后再次渲染一次,也不要在 ready 被拒绝时重复调用带副作用的提交函数。视觉跳过和业务更新失败需要分别解释。

改动点:在更新回调里提交新视图
先准备稳定的输入区、切换按钮和内容区:
样式只给内容区一个独立名称。本例依靠浏览器默认过渡效果,不增加自定义动画:
/* 本例只给一个稳定容器命名,避免同屏名称重复。 */
#view { view-transition-name: content-panel; }
把脚本放在上述元素之后,或作为延迟执行的脚本加载:
const draft = document.querySelector("#draft");
const toggle = document.querySelector("#toggle");
const view = document.querySelector("#view");
const status = document.querySelector("#status");
// 业务状态有自己的对象,不从快照或过渡伪元素里读取。
const state = { page: "概览", draft: draft.value };
let busy = false;
let commitCount = 0;
function render() {
const paragraph = document.createElement("p");
paragraph.textContent = `${state.page}:${state.draft}`;
view.replaceChildren(paragraph);
}
draft.addEventListener("input", () => {
state.draft = draft.value;
render(); // 普通输入不必强行附带页面过渡。
});
toggle.addEventListener("click", async () => {
if (busy) return; // 本例采用忽略进行中重复请求的策略。
busy = true;
toggle.disabled = true;
draft.disabled = true;
const nextPage = state.page === "概览" ? "详情" : "概览";
const commit = () => {
state.page = nextPage;
render();
commitCount += 1;
};
try {
const reduceMotion = matchMedia("(prefers-reduced-motion: reduce)").matches;
if (typeof document.startViewTransition !== "function" || reduceMotion) {
commit(); // 降级仍提交同一份业务更新。
} else {
const transition = document.startViewTransition(commit);
// 动画准备失败不代表需要再次执行 commit。
transition.ready.catch(() => {});
// 立即订阅两项结果,更新失败时在外层显式报告。
await Promise.all([
transition.updateCallbackDone,
transition.finished,
]);
}
status.textContent = `视图已更新,提交次数:${commitCount}`;
} catch (error) {
console.error("视图更新失败", error);
status.textContent = "更新失败,请检查业务渲染;不要自动重复提交。";
} finally {
busy = false;
toggle.disabled = false;
draft.disabled = false;
}
});
render();
busy 是本例选择的并发请求策略:进行中不再接受另一次切换,同时暂时禁用输入,避免用户在等待旧快照期间继续改动本例草稿。它不是所有产品都必须采用的策略;需要排队或最后一次请求优先时,应另行定义。
代码没有重建输入框,state.draft 也不因切换而清空。业务复杂时,可把 state 换成框架状态或应用存储,但仍需保证 DOM 提交发生在所用框架真正完成渲染的边界里,而非仅仅调用异步更新函数就当作完成。

复现方法:比较动画与直接更新分支
将标记、样式和脚本组合到自己的示例页后,可以按下面几种场景观察。它们是供读者复现的检查点,不是本文已经执行的测试报告。
| 场景 | 应观察的业务行为 |
|---|---|
| 输入备注后切换概览和详情 | 草稿文本保留,只改变 page |
| 一次切换完成 | commitCount 增加一次 |
| 切换过程中再次点击 | 本例忽略重复请求 |
| API 不可用 | 直接提交同一更新 |
| 用户偏好减少动态效果 | 跳过动画但保留更新能力 |
故意让业务 render 抛错时,应看到失败提示而不是重复提交。真实项目还应定义业务状态的回滚或恢复方式;View Transitions 不会替应用撤销已经执行的 DOM 或数据副作用。
结果对比:完成信号各自代表什么
| 信号 | 适合表达什么 | 不要误认为 |
|---|---|---|
| updateCallbackDone | 更新回调已完成 | 过渡动画已播完 |
| ready | 过渡伪元素已准备好,可开始动画 | 表单已保存到服务器 |
| finished | 过渡完成,或跳过后达到结束状态 | 必须重新提交业务更新 |
本例同时等待更新与过渡结束,随后在 finally 释放 busy 和按钮。ready 的拒绝被单独订阅,不触发提交重试;更新错误仍由 updateCallbackDone 和外层处理报告。业务若需要把失败状态反馈给路由层,还应把这一结果纳入自己的调用约定。
这个改动改善的是职责清晰度和更新次数的可解释性。没有负载数据时,不能把增加 View Transitions 描述为速度优化;视觉连续性与实际响应耗时是两项不同的产品指标。
边界条件与常见问题
刷新页面后草稿会自动恢复吗?
不会。本例只有内存中的 state;跨刷新需要应用自行选择持久化或服务端恢复方案。快照不能充当业务数据库。
skipTransition 会取消 DOM 更新吗?
它跳过动画部分,不跳过用于更新 DOM 的回调。不要把它当作网络请求取消或数据回滚按钮。
为什么同名元素可能导致过渡不能播放?
命名元素需要满足过渡快照的匹配约束,本例只给一个稳定内容容器命名。列表里为多个同时可见元素写相同名称,不能作为“保留所有节点”的办法。
跨文档导航能直接照抄吗?
本文只使用 startViewTransition 的同文档路径。跨文档导航有不同的启用条件和生命周期;路由状态、草稿以及返回时的滚动位置仍需应用定义,不应把此示例当完整跨页恢复方案。
浏览器一定支持全部扩展能力吗?
本例先检测使用的方法,并始终提供直接更新分支。不要因为看到接口存在,就推断所有扩展参数、跨页能力和目标设备组合完全一致。
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
234 收藏
-
476 收藏
-
356 收藏
-
401 收藏
-
361 收藏
-
245 收藏
-
120 收藏
-
282 收藏
-
284 收藏
-
480 收藏
-
214 收藏
-
496 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习