Swup 入门:无刷新页面转场
传统多页站每次点击都是白屏一闪再整页重载。Swup 让页面切换变成平滑的转场动画——内容换了,页面的「魂」还在。
MPA 的体验困境与 Swup 的方案
本站是 Astro 多页应用(MPA),每个页面独立 URL、独立加载——SEO 和首屏性能好,但页面间切换是浏览器原生跳转:旧页销毁、白屏、新页从零渲染。这在内容站上表现为「点文章 → 闪一下 → 文章出现」,那种「闪一下」割裂感明显。
Swup 的做法:劫持站内链接的点击,用 fetch 拉取新页 HTML,只替换 <main> 等指定容器的内容,其余(导航栏、侧栏、背景)根本不动,再配上转场动画。视觉效果接近 SPA,架构上仍是纯 MPA。
点击链接 → Swup 拦截
→ fetch 新页面 HTML
→ 播放退场动画(旧内容淡出)
→ 替换 main 容器内容 + 更新 URL 和标题
→ 播放入场动画(新内容淡入)
在 Astro 里接入
Astro 有官方集成,几乎零配置:
pnpm add @swup/astro
// astro.config.mjs
import swup from '@swup/astro';
export default defineConfig({
integrations: [
swup({
theme: 'fade', // 内置转场主题:fade/slide/overlay
animation: true,
containers: ['main'], // 只替换 main 容器,其他不动
updateHead: true, // 同步 head 里的 title/meta
updateBodyClass: true, // 同步 body class(主题态等)
globalInstance: true,
}),
],
});
装完即生效——所有站内链接的跳转自动变成带转场的无刷新切换。本站就是这么接的:containers: ['main'] 保证顶栏、侧栏、背景动效跨页面持续存在,页面切换只有内容区淡入淡出。
它真正解决的问题:状态存活
转场动画是「面子」,状态存活是「里子」:
- 音乐播放器(本站的背景音乐):页面切换不停播——原生跳转必断;
- 滚动位置:列表滚到 2000px 点开文章再返回,列表还在 2000px;
- JS 状态:侧栏折叠态、过滤器选择,跨页面保持;
- 动画连续性:背景粒子、看板娘这些全局动效不再每页重建。
这些在纯 MPA 里要么做不到要么要靠 localStorage 模拟,Swup 下天然成立。
脚本执行:页面切换后 JS 怎么办
MPA 换成无刷新切换后有个新问题:页面里的 <script> 不会自动重新执行。Astro 集成默认会处理常规脚本,但挂载逻辑最好显式监听 Swup 的生命周期:
document.addEventListener('swup:page:view', () => {
// 每次新页面展示后触发——重新初始化该页专属的组件/脚本
initPageScripts();
});
常用生命周期:swup:page:view(新页就绪)、swup:visit:start(开始跳转,可做统计埋点)。本站的部分交互初始化就挂在这类钩子上,保证每次「虚拟跳转」后页面功能完整。
哪些跳转不该被拦截
不是所有链接都该无刷新化。Swup 默认会放行:外链、target="_blank"、带 data-no-swup 属性的链接、下载链接、管理后台等跨应用跳转。原则是——同一个应用体验内的跳转交给 Swup,离开这个体验的跳转保持原生。本站的后台入口、外部友链就都是原生跳转。
与浏览器行为的兼容
Swup 处理了前进/后退(popstate)、锚点、URL 更新这些浏览器基础行为,但有两点要注意:
第三方统计:原生跳转浏览器自动发 pageview,Swup 切换不会——要在 swup:page:view 里手动上报 PV,否则统计量会骤降。
资源加载:新页面用到的 CSS/JS 要么全站共享(已被首页加载),要么在 head 同步逻辑里补加载(updateHead: true 处理)。各页差异大的站要留意「切页后脚本缺失」的报错。
小结
Swup 让 MPA 获得三种东西:转场动画(体验)、状态存活(功能)、更少的重复请求(性能),而付出的代价是几行配置加一点生命周期适配。对内容型多页站,这是性价比极高的体验升级——Astro 官方集成的存在,让接入成本几乎只有「装一下」。