2x4 VueRouter - 进阶 - 过渡动效

1,273 阅读2分钟

VueRouter 基础教程系列 🎉

简介

路由的过渡效果指的是为路由组件增加转场效果。 在 VueRouter 3.x 中可以直接使用 <transition> 组件包裹 <rotuer-view> 组件实现路由的过渡效果。 在 VueRouter 4.x 则需要借助 <router-view> 组件的 v-slot API 方能实现路由的过渡效果。

<router-view>v-slot API

<router-view> 组件的作用域插槽会暴露出当前要渲染的路由组件实例,然后使用动态组件技术来接收这个路由组件实例,最后再用 <transition> 组件来包裹这个动态组件,便可 VueRouter 4.x 下实现路由的过渡效果。

<!--App.vue-->
<router-view v-slot="{ Component }">
    <transition appear >
      <component :is="Component" />
    </transition>
</router-view>

动态过渡效果

利用路由元信息可以附加自定义数据的能力,我们便可以在路由配置(routes)中为每个路由组件定义不同的过渡效果。

核心秘诀: 路由元信息 + 动态过渡 name 属性值。

也可以在任何一个路由钩子中通过一定的关系运算动态生成过渡名称。

router.afterEach((to, from) => {
  const toDepth = to.path.split('/').length
  const fromDepth = from.path.split('/').length
  to.meta.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})
<router-view v-slot="{ Component, route }">
    <transition appear :name="route.meta.transition" >
      <component :is="Component" />
    </transition>
</router-view>

一般更多会在全局钩子中进行判断,例如 beforeEachbeforeResolveafterEach,当然你可以通过响应式变量使用数据驱动的方式来修改 name 属性的值。

一个简单的示例

让我们定义一个淡入淡出的路由过渡效果,为了不让我们路由组件产生闪烁的视觉问题,我们必须通过 mode 属性来定义过渡进入与离开的顺序。

<router-view v-slot="{ Component }">
    <transition appear mode="out-in" >
      <component :is="Component" />
    </transition>
</router-view>
.v-enter-from,.v-leave-to{
  opacity: 0;
}
.v-enter-active,.v-leave-active{
  transition: all .35s;
}

现在,让我们升级下过渡效果,在 淡入淡出 的基础上再增加一个滑动效果,并且我们希望同时能看到下一个路由组件滑动进入、当前路由组件滑动离开。

为了实现同时显示两者的效果,我们必须要删除 mode 属性,并为每个路由组件设置 absolute 定位,以避免路由切换时的位置闪烁问题。

<router-view v-slot="{ Component }">
    <transition appear name="slide-fade" >
      <component :is="Component" />
    </transition>
</router-view>
.v-enter-from {
  position: absolute;
  opacity: 0;
  right: -100%;
}

.v-enter-to {
  right: 0;
}

.v-leave-from {
  left: 0%;
}
.v-leave-to {
  left: -100%;
  opacity: 0;
}

.v-enter-active,
.v-leave-active {
  position: absolute;
  transition: all 0.35s ease-in-out;
}

缓存路由组件

相同的思路,我们也可以结合 <keep-alive> 组件一起使用,缓存路由组件实例。

 <router-view v-slot="{ Component, route }">
  <transition :name="route.meta.transition || 'fade'" mode="out-in">
    <keep-alive>
      <component
        :is="Component"
        :key="route.meta.usePathKey ? route.path : undefined"
      />
    </keep-alive>
  </transition>
</router-view>