路由过渡动画
本教程共 50 篇 · 第 21 篇 · 更新于 2026-08-08 · 约 6 分钟阅读
本节目标:理解 Nuxt 的页面过渡机制,能给页面切换加上淡入淡出等动画,并知道单根元素的硬性要求。
21-1
页面切换时那种「旧页淡出、新页淡入」的效果,Nuxt 是借用了 Vue 自带的 <Transition> 组件实现的。Nuxt 在 <NuxtPage> 外层自动包了一层 <Transition>,并通过 transition 的 name 去匹配你写的 CSS 类,从而产生动画。
也就是说,你不用自己写 <Transition> 标签,只要告诉 Nuxt「这个过渡叫什么名字」,再补上对应的 CSS,动画就出来了。
Warning被动画的页面或布局必须有单一根元素。如果一个
.vue模板里有多个并列的根节点(或用 HTML 注释当占位),Vue 的<Transition>无法判断动画套在哪个节点上,过渡就不会跑,开发模式下 Nuxt 还会给出警告。最稳妥的做法是用一个<div>把内容包起来。注意:HTML 注释在模板里也算一个「元素」,别留着。
21-2
先在 nuxt.config 里给所有页面设一个默认过渡名(这里叫 page),并指定模式 out-in(先让旧页完全离开,再让新页进入):
export default defineNuxtConfig({
app: {
pageTransition: { name: 'page', mode: 'out-in' },
},
})
然后在 app.vue 里写上对应的 CSS。Vue 的过渡约定:进入时加 .page-enter-from / .page-enter-active / .page-enter-to,离开时加 .page-leave-from / .page-leave-active / .page-leave-to。我们一般只写 from(起始态)和 active(过渡过程),让 to 回到默认样式。
<template>
<NuxtPage />
</template>
<style>
.page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
}
</style>
这样,所有页面切换都会带上「0.4 秒淡出 + 模糊」的效果。
Note如果你在切换时同时换了布局又换了页面,Nuxt 会优先播放「布局过渡」而不是这里设的「页面过渡」。那种情况请改用第 16 章布局 + 下面的
layoutTransition配置。
21-3
想让某一页用不同的动画?在那页的 definePageMeta 里覆盖 pageTransition 的 name 即可,其它页不受影响。
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'rotate',
},
})
</script>
对应的 CSS 用 rotate- 前缀:
<style>
.rotate-enter-active,
.rotate-leave-active {
transition: all 0.4s;
}
.rotate-enter-from,
.rotate-leave-to {
opacity: 0;
transform: rotate3d(1, 1, 1, 15deg);
}
</style>
Tip改了
name,CSS 类名也要跟着改,否则动画不生效。这是最容易忘的一点:名字和类名必须一一对应。
21-4
布局也能有过渡,配置项和页面几乎一样,只是前缀变成 layout:
export default defineNuxtConfig({
app: {
layoutTransition: { name: 'layout', mode: 'out-in' },
},
})
CSS 对应 .layout-enter-active 等类。某个页面不想用过渡,把值设 false:
<script setup lang="ts">
definePageMeta({
pageTransition: false,
layoutTransition: false,
})
</script>
全局关闭也行,在 nuxt.config 里把两项都设 false 即可。
21-5
想要更花哨的动画(比如用 GSAP 这类 JS 动画库),可以用 onBeforeEnter / onEnter / onAfterEnter 等 JS 钩子:
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'custom-flip',
mode: 'out-in',
onBeforeEnter: (el) => {
console.log('进入之前...', el)
},
},
})
</script>
还能根据路由动态切换动画方向:在 definePageMeta 里写一个 middleware 函数,根据 to / from 的参数大小,把 to.meta.pageTransition.name 改成 slide-left 或 slide-right,做出「前进左滑、后退右滑」的体验。
21-6
从 Nuxt 4.4 起,还实验性地支持浏览器原生的 View Transitions API(能跨页面对不相关元素做过渡)。在 nuxt.config 开启:
export default defineNuxtConfig({
experimental: {
viewTransition: true,
},
})
取值可为 false、true 或 'always'。true 会在用户系统开启「减少动态效果」时自动禁用(推荐),'always' 则强制开启。自 Nuxt 4.4 起还支持 view transition types,能为「前进/后退」配置不同 CSS 动画;这些类型可在 definePageMeta 的 viewTransition 里按页设置。
WarningView Transitions API 仍是实验特性,且需浏览器支持。它和 Vue 的
pageTransition是两套机制:用了原生 API,可以考虑用全局中间件把 Vue 过渡关掉,避免两者打架。生产环境使用前请确认目标用户浏览器兼容性。
21-7
pageTransition / layoutTransition 与 definePageMeta 配置方式在 Nuxt 3、Nuxt 4 一致。View Transitions API 是 Nuxt 4.4+ 的实验特性,Nuxt 3 不支持;本教程基线 Nuxt 4.5.2 已可用,但仍建议谨慎用于生产。
21-8
前面例子里 mode: 'out-in' 表示「先出后进」。Vue 的过渡默认是「新旧同时过渡」(进出重叠)。页面切换时,两个页面同时占屏容易布局抖动,所以 Nuxt 官方示例都用 out-in,等旧页完全离开再让新页进入,视觉更稳。
21-9
若动画没出现,按顺序检查这几点:页面是否有单一根元素(最常见坑);name 和 CSS 类名前缀是否一致;pageTransition 是否在某处被设成了 false;<NuxtPage> 是否真的在 app.vue 里。开发模式下 Nuxt 对「多根元素」会直接警告,看到警告先解决它。
Tip过渡和
key配合很好用:给<NuxtPage>设不同key,能强制在觉得「需要重播动画」的时机重新触发过渡,做出列表项切换、卡片翻面等效果。
21-10
Nuxt 的过渡靠 Vue 的 <Transition> 实现,而 <Transition> 的动画完全由 CSS 类驱动。规则是:你给过渡起一个 name(比如 page),Vue 就会在动画不同阶段自动给元素加上 name-enter-from、name-enter-active、name-enter-to、name-leave-from、name-leave-active、name-leave-to 这六个类。你通常只需写 from(起始状态)和 active(过渡时长与曲线),to 回到元素默认样式即可省略。
对应关系就一句话:CSS 类名的 前缀 = 你在 pageTransition 里设的 name。所以:
name: 'page'→ 写.page-enter-from等;name: 'rotate'→ 写.rotate-enter-from等;name: 'slide-left'→ 写.slide-left-enter-from等。
名字写错,或 CSS 前缀对不上,动画就完全不触发——这是新手排错第一要看的地方。回到第 21 章开头的全局例子:我们在 nuxt.config 里 name: 'page',app.vue 里写的正是 .page-enter-from,前后对上了,动画才生效。
Tip如果你改了
name却忘了改 CSS 前缀,开发模式下 Nuxt 不会报错,只是「切页毫无动画」。遇到这种情况,先核对一遍name和类名前缀是否一字不差。
21-11
pageTransition(页面级)作用在 <NuxtPage> 渲染的那块区域:切换页面时,旧页淡出、新页淡入。
layoutTransition(布局级)作用在 <NuxtLayout> 包裹的那一层:切换布局时(比如从前台 default 跳到后台 admin),旧外壳淡出、新外壳淡入。
两者的核心区别是作用范围:页面级只动「内容区」,外壳不动;布局级动的是「整个外壳」。如果你同时换了布局又换了页面,Nuxt 优先播布局级过渡——因为外层先变、内层再变更符合直觉。
Note不是每次切换都有布局过渡。只有「布局名变了」才会触发
layoutTransition;同一布局内换页面,播的是pageTransition。别把两者搞混,否则会疑惑「我明明设了 layoutTransition 怎么没反应」——很可能你只是换了页面、没换布局。
21-12
除了淡入淡出,这几个配方最常用,机制完全一致:设好 name,再写对应的 from / leave-to 即可。
- 左滑进场:
.slide-left-enter-from { transform: translateX(100%) }、.slide-left-leave-to { transform: translateX(-100%) } - 上浮进场:
.fade-up-enter-from { opacity: 0; transform: translateY(20px) } - 缩放进场:
.zoom-enter-from { opacity: 0; transform: scale(0.95) }
配合前面讲过的「用中间件动态切换 pageTransition.name」,你就能做出「前进左滑、后退右滑」的体验:监听 to 和 from 在路由树里的深度,深了用 slide-left,浅了用 slide-right。
Warning这些配方都依赖「页面有单一根元素」。如果页面模板里根节点不止一个,Vue 不知道把
transform套到哪个节点上,动画依然会失效。写动画前先确认根元素唯一,这一条比选哪个配方都重要。
21-13
页面过渡靠 Vue 的 <Transition> + name 对应的 CSS 类实现;name 是什么,CSS 类名前缀就得是什么。全局在 nuxt.config 设 pageTransition/layoutTransition,单页用 definePageMeta 覆盖;页面级动内容区、布局级动外壳。记牢「页面必须有单一根元素」这条铁律。到本章为止,视图与路由这一大块的九个知识点就讲完了。