页面退出动画仅适用于单页内跳转场景,真关闭标签页或刷新时因浏览器策略限制无法实现;需通过class切换触发transition,配合transitionend时机控制dom移除或路由跳转,并注意进出动画的时序管理与方向适配。

页面退出动画不能靠 beforeunload 触发 CSS 过渡
浏览器在 beforeunload 事件中禁止执行耗时操作,且一旦该事件触发,页面会立刻进入“终止流程”,DOM 不再重绘,transition 和 @keyframes 都不会播放。你看到的“闪一下就关了”,就是这个原因。
真正能用的退出动画,只适用于「单页内跳转」场景——比如用 history.pushState() 切换路由,或点击按钮隐藏当前模块、展示新模块。这种退出是可控的、可等待的。
- 真关闭标签页 / 刷新 / 输入新 URL → 无法做退出动画(浏览器策略限制)
- SPA 内部路由跳转(如从 /home → /about)→ 可以做退出动画
- 模态框关闭、抽屉收起、tab 切换 → 属于 DOM 显隐控制,完全可做
transition 做退出动画必须配合显隐状态切换
直接给元素加 opacity: 0 或 transform: scale(0) 不会触发动画,因为浏览器没检测到“状态变化”。必须通过 class 切换来触发重排/重绘。
正确写法示例:
/* CSS */
.exit-target {
opacity: 1;
transform: translateX(0);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.exit-target.is-exiting {
opacity: 0;
transform: translateX(-20px);
}
JS 控制时机:
function startExit(el) {
el.classList.add('is-exiting');
// 等待动画结束再移除元素或跳转
el.addEventListener('transitionend', () => {
el.remove(); // 或 history.pushState(...) / router.push(...)
}, { once: true });
}
- 别用
display: none:它不触发transition - 别在
transitionend里做异步跳转(如fetch后再跳),会导致动画卡在半途 - 移动端要注意
will-change: transform提升合成层,避免动画掉帧
退出动画和进入动画要配对管理,否则容易错乱
一个常见错误是:新内容已插入并开始进入动画,旧内容还没完成退出,导致视觉重叠或 z-index 错位。
推荐结构:
- 用统一容器包裹所有页面区域(如
<main id="router-view"></main>) - 退出前先给旧内容加
is-exiting,再插入新内容并加is-entering - 等旧内容
transitionend后,再清理旧 DOM;新内容动画由独立 class 控制 - 如果用
animation(非transition),务必设animation-fill-mode: forwards,否则动画结束瞬间样式会回退
关键点:退出和进入不是“同时开始”,而是“退出启动 → 退出完成 → 进入启动”,中间有明确时序依赖。
history.back() / 浏览器前进后退时的退出动画最难搞
监听 popstate 很容易,但你无法预知用户是从哪一页来的,也就没法提前准备“退出方向”(比如从 /about 回 /home,/about 应该向右滑出,/home 向左滑入)。
可行方案:
- 记录上一页路径(
sessionStorage.setItem('prevPath', location.pathname)),在popstate中读取并动态加方向类(如exit-to-right) - 统一用淡出+淡入,回避方向判断(最稳妥)
- 放弃对
popstate的动画支持,只对主动点击的链接做动画(降低复杂度)
注意:getComputedStyle(el).opacity 在 popstate 触发瞬间可能还是 1,需确保 class 添加和 transition 触发逻辑在同一次渲染帧内完成,否则动画会跳过。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











