Code前端首页关于Code前端联系我们

Vue Router View 怎么结合 Transition 做页面切换动效?

terry 10小时前 阅读数 11 #Vue

想给 Vue 项目里的页面切换加上丝滑动效,用 Vue Router 的 <router-view> 结合 <Transition> 咋整?这篇从基础用法、进阶技巧到实际场景,一步步讲清楚,帮你把页面跳转做成用户爱点的“视觉糖”。

基础逻辑: 咋配合?

先理清俩组件的角色:<router-view> 是路由匹配后渲染组件的“出口”,<Transition> 是 Vue 专门处理元素/组件「进入、离开动画」的内置组件,要让页面切换有动画,核心是<router-view> 包进 <Transition>,利用 Vue 自动生成的过渡类名写 CSS 动画。

举个最简例子:

<template>
  <Transition name="fade" mode="out-in">
    <router-view></router-view>
  </Transition>
</template>
<style scoped>
/* 进入前、离开后:透明度 0 */
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}
/* 进入中、离开中:过渡效果 */
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s ease;
}
/* 进入后:透明度 1(可省略,默认继承) */
.fade-enter-to {
  opacity: 1;
}
/* 离开前:透明度 1(可省略,默认继承) */
.fade-leave-from {
  opacity: 1;
}
</style>

这里关键是 mode="out-in" ——它控制组件切换时序:先让「离开的组件」完成离开动画,再让「进入的组件」执行进入动画,如果不加 mode,两个组件会同时动,容易出现重叠、闪烁,体验拉垮。

不同动效咋设计?(淡入淡出、滑动、缩放…)

页面切换动效不止淡入淡出,不同场景适合不同风格,下面分效果讲实现逻辑:

淡入淡出(基础款,适合后台管理)

就是上面的例子,靠 opacity 变化实现,优点是简单、不抢焦点,适合后台系统这类功能型页面,让切换有“呼吸感”但不花哨。

左右滑动(模拟 APP 原生切换,适合 H5)

要让页面像手机 APP 一样左右滑,得解决两个问题:判断滑动方向(前进/后退)、给不同方向加不同动画

步骤 1:给 <Transition> 绑定动态 nameslide-left(前进时从右滑入)或 slide-right(后退时从左滑入)。
步骤 2:在路由守卫里判断是“前进”还是“后退”,设置动画名。

代码示例:

<template>
  <Transition :name="transitionName" mode="out-in">
    <router-view></router-view>
  </Transition>
</template>
<script setup>
import { ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()
const transitionName = ref('slide-left') // 默认前进动画
// 监听路由变化,判断是前进还是后退
watch(
  () => route.fullPath,
  (newPath, oldPath) => {
    // 简单判断:新路由在历史记录中的位置是否更靠后
    const isForward = router.options.history.stack.some((item, index) => {
      if (item === oldPath) return router.options.history.stack[index + 1] === newPath
    })
    transitionName.value = isForward ? 'slide-left' : 'slide-right'
  }
)
</script>
<style scoped>
/* 前进:新页面从右侧滑入,旧页面滑出到左侧 */
.slide-left-enter-from {
  transform: translateX(100%);
}
.slide-left-enter-active, .slide-left-leave-active {
  transition: transform 0.3s ease;
}
.slide-left-leave-to {
  transform: translateX(-100%);
}
/* 后退:新页面从左侧滑入,旧页面滑出到右侧 */
.slide-right-enter-from {
  transform: translateX(-100%);
}
.slide-right-leave-to {
  transform: translateX(100%);
}
.slide-right-enter-active, .slide-right-leave-active {
  transition: transform 0.3s ease;
}
</style>

这种方式靠路由历史记录判断方向,简单场景够用;复杂场景(比如带 tabs 的多层级路由),可以给路由配置 metadepth 字段,通过比较 depth 判断前进后退。

缩放 + 渐变(强调层级,适合弹窗/详情页)

比如从商品列表点进详情页,让详情页从列表项位置“放大展开”,退出时“缩小收起”,这种动效需要动态计算起始位置,但基础缩放框架可以这么写:

<Transition name="zoom" mode="out-in">
  <router-view></router-view>
</Transition>
<style scoped>
.zoom-enter-from {
  transform: scale(0.8);
  opacity: 0;
}
.zoom-enter-active {
  transition: transform 0.3s ease, opacity 0.3s ease;
}
.zoom-leave-to {
  transform: scale(0.8);
  opacity: 0;
}
.zoom-leave-active {
  transition: transform 0.3s ease, opacity 0.3s ease;
}
</style>

如果要精准定位缩放起点,得结合 JS 获取列表项坐标,动态设置 transform-origin 和初始 scale,但核心还是 <Transition> 的类名控制。

和普通 有啥不一样?

很多同学用惯了给单个元素加 <Transition>,遇到 router-view 容易踩坑,核心区别在“切换的是整个组件”

  • 普通 <Transition>:包裹的是单个元素(如按钮、div),切换时只有一个元素的“进入/离开”。
  • router-view + Transition:切换时是两个组件(旧组件离开,新组件进入),所以必须用 mode 控制时序(out-in/in-out),否则两个组件同时动画会打架。

举个反面例子:如果不给 <Transition>mode,旧组件还没完全离开,新组件已经开始进入,页面会瞬间出现两个组件重叠,动画逻辑混乱。

动态过渡咋玩?(根据路由、状态变效果)

不想所有页面切换都用同一种动画?可以让动效随路由、用户操作动态变化,这才是“定制感”的关键。

不同路由用不同动效(路由 meta 配置)

给路由规则加 meta.transition,让不同页面走不同动画:

路由配置(router.js):

const routes = [
  {
    path: '/home',
    component: Home,
    meta: { transition: 'fade' } // 首页用淡入淡出
  },
  {
    path: '/detail',
    component: Detail,
    meta: { transition: 'zoom' } // 详情页用缩放
  }
]

组件中动态绑定 name

<Transition :name="$route.meta.transition" mode="out-in">
  <router-view></router-view>
</Transition>

根据用户操作变方向(比如返回按钮)

如果页面有自定义返回按钮(不是浏览器默认返回),可以用状态管理控制动画方向,比如用 Pinia 存 isBack

<!-- 自定义返回按钮 -->
<button @click="goBack">返回</button>
<script setup>
import { useTransitionStore } from '@/stores/transition'
const transitionStore = useTransitionStore()
const goBack = () => {
  transitionStore.setIsBack(true) // 标记为后退
  router.back()
}
</script>

<Transition> 组件中根据 isBack 换动画名:

<Transition :name="isBack ? 'slide-right' : 'slide-left'" mode="out-in">
  <router-view></router-view>
</Transition>
<script setup>
import { computed } from 'vue'
import { useTransitionStore } from '@/stores/transition'
const transitionStore = useTransitionStore()
const isBack = computed(() => transitionStore.isBack)
</script>

性能咋优化?别让动效拖慢页面!

动效好看,但卡成 PPT 就适得其反了,这几个优化点要记牢:

能用硬件加速,就不用“重绘属性”

CSS 动画优先用 transform(平移、缩放、旋转)和 opacity ——这俩属性触发合成层渲染,浏览器会用 GPU 加速,性能好,尽量别用 topleft 这类会触发“重排+重绘”的属性,尤其是复杂页面。

比如滑动动效,用 transform: translateX() 代替 left: 100%

路由组件懒加载,避免动画延迟

如果路由组件体积大,切换时才加载会导致动画“卡一下”,用路由懒加载提前拆分代码:

const Home = () => import('@/views/Home.vue')
const Detail = () => import('@/views/Detail.vue')
const routes = [
  { path: '/home', component: Home },
  { path: '/detail', component: Detail }
]

keep-alive 缓存组件,减少重复渲染

如果页面切换后需要保留组件状态(比如表单输入、滚动位置),用 <keep-alive> 包裹 <router-view>,配合 Transition

<Transition name="fade">
  <keep-alive>
    <router-view></router-view>
  </keep-alive>
</Transition>

这样组件切换时不会销毁重建,动画更流畅,但要注意:keep-alive 会保留组件状态,业务逻辑里要处理“是否需要重置”的情况。

控制动画时长,别太长

用户对“页面切换延迟”很敏感,动画时长建议控制在200 - 300ms,太长会觉得“卡”,太短没质感,得平衡。

实际场景案例——不同项目咋用?

光讲技术不够,结合场景才知道咋落地:

后台管理系统:轻量反馈,别喧宾夺主

后台页面以功能为主,动效要“隐形”但有反馈。

  • 侧边栏切换页面:用淡入淡出 + 轻微滑动(X 轴平移 5% + 透明度变化),让用户感知“页面变了”,但不分散注意力。
  • 权限级页面切换:比如从普通页面进入管理员页面,用缩放 + 边框高亮,暗示“层级提升”。

H5 活动页:强互动,模拟原生体验

H5 活动页靠互动感留住用户,动效要“花哨但不晕”:

  • 多页表单(比如答题、问卷):用上下滑动 + 进度条联动,让用户像翻书一样操作,配合进度条动画,增强参与感。
  • 节日活动页(618、双 11):用卡片翻转 + 渐变,切换页面时带 3D 翻转效果,营造“拆礼盒”的惊喜感。

电商 APP 内嵌 H5:强化流程连贯性

从 APP 原生页跳转到 H5 页(比如商品详情→活动页),动效要和 APP 风格统一:

  • 详情页→购物车:用放大 + 渐变,让购物车从商品图位置“弹出来”,退出时“缩回去”,强化“添加→结算”的流程感。
  • 列表→详情:用左右滑动 + 图片无缝衔接(列表项图片和详情页图片位置对齐),模拟 APP 原生切换的丝滑感。

常见坑点咋避?

最后避坑指南,少走弯路:

动画不触发?检查包裹结构

<Transition> 必须直接包裹 <router-view>,中间不能插其他元素(div 套 router-view 再塞 Transition 里,不行!),正确结构:

<Transition>
  <router-view></router-view>
</Transition>

组件重叠、闪烁?忘加 mode 属性

只要是 router-view 的过渡,必须加 mode="out-in"mode="in-out"优先选 out-in(先离开再进入),符合用户对“页面切换”的认知。

CSS 类名写错?分清 Vue2 和 Vue3

Vue3 中,过渡类名是 xxx-enter-fromxxx-leave-to;Vue2 中是 xxx-enterxxx-leave-to,如果项目是 Vue2,类名要对应改,否则动画不生效。

动态过渡不生效?响应式数据没绑定好

如果用变量控制 transitionName,必须是响应式数据ref/reactive),比如用普通变量存 name,Vue 检测不到变化,动画不会更新。

页面切换动效不是“炫技”,而是用视觉反馈降低用户认知成本,把 <router-view><Transition> 玩明白,既能让产品更有质感,又能让用户操作更流畅——下次改需求时,别再只会写 alert 提示“页面切换啦”,用动效悄悄提升体验才是高手~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门