MPA页面过渡需服务端壳页+客户端动态替换content,禁用display动画,用opacity/visibility/transform实现;Safari需will-change+backface-visibility优化。

MPA(多页应用)中加页面过渡动画,本质是“拦截跳转 → 淡出旧页 → 加载新页 → 淡入新页”,不能靠 history.pushState 或单页路由那一套——它天然不适用。浏览器整页刷新时,DOM 一卸载就没了,所有 JS 监听和 CSS 类都会失效。
为什么直接给 body 加 transition 没用
很多人试过在 body 上写 transition: opacity 0.4s,再用 window.location.href = 'page2.html' 跳转,结果动画根本没跑。原因很直接:
-
body元素在跳转瞬间被浏览器强制销毁,transition 连触发机会都没有 -
display: none、visibility: hidden等状态切换在跨页面时无法保持上下文 - 新页面加载后,样式重置,旧页面的动画类名、内联样式全丢失
真正可行的 MPA 过渡方案:服务器端注入 + 客户端钩子
必须让两个页面“共存一小段时间”。主流做法是服务端输出一个统一壳页(shell),把实际内容作为 <div id="content"> 的 innerHTML 动态替换,同时保留 <code> 和基础布局。这已接近 SPA,但仍是 MPA 架构(每个 URL 对应真实 HTML 文件)。
实操要点:
立即学习“前端免费学习笔记(深入)”;
- 所有页面 HTML 都要包含相同结构:
<div id="content">...</div>,且不带外层标签(由壳页提供) - 用
fetch()加载目标页面,提取#content的 innerHTML,而非整个文档 - 淡出时对当前
#content添加page-exit类,监听transitionend事件后再替换内容 - 淡入前,先将新内容插入 DOM 并设为
opacity: 0; transform: translateY(10px),再加page-enter-active类触发过渡 - 务必在添加类后执行
void element.offsetWidth强制重排,否则动画不触发
iOS 和 Safari 下动画卡顿的硬解法
旧版 Safari(尤其 iOS ≤16)对 transform + opacity 合成优化差,常出现掉帧或首帧延迟。关键补丁只有三个:
- 给动效容器加
will-change: transform, opacity(仅在动画前设置,动画结束立即移除) - 加
backface-visibility: hidden,强制开启 GPU 图层 - 避免在动画元素上使用
filter、box-shadow或大图背景,它们会破坏合成层
注意:will-change 不是“加了就变快”的魔法开关,滥用反而降低性能;它只应在动画开始前 1–2 帧动态添加,例如:
element.style.willChange = 'transform, opacity';
setTimeout(() => {
element.classList.add('page-enter-active');
}, 16);
别碰 display: none / block 过渡
这是最常见也最顽固的坑。有人写:
.page { transition: all 0.3s; }
.page.hidden { display: none; }
结果无论怎么切类,动画都不动。因为 display 属性不可过渡——它没有中间值。浏览器要么渲染,要么彻底剔除,transition 根本无从插手。
正确替代方式只有两个:
- 用
opacity+visibility:前者控制透明度,后者控制是否参与渲染流(visibility: hidden仍占位,display: none不占位) - 用
transform: translateX(100%)移出视口,配合pointer-events: none防止误触
所有过渡都必须基于可动画属性,而 display、height(值为 auto 时)、width(同理)都不在此列。
MPA 过渡真正的复杂点不在代码量,而在生命周期管理:你得精确控制旧页何时卸载、新页何时可交互、动画中断时如何恢复、以及 SEO 和 history 栈的一致性。这些没法靠一个 CSS 类解决,得靠状态机驱动。



















