Skip to content

router-view && keep-alive && transition

概览

由于Vue3中函数式组件的变更,RouterView组件与KeepAliveTransition 共同使用时不再是简单的被它们包裹。相反,我们需要一个方式直接提供要被RouterView渲染的组件。

vue

<router-view v-slot="{ Component }">
<transition :name="transitionName" mode="out-in">
  <component :is="Component"></component>
</transition>
</router-view>

动机

如在vuejs/core#906 中描述的那样,我们需要一个新的API允许KeepAlive和其他组件搭配RouterView 使用可以接收slot。为了实现上述的行为,我们需要用RouterView直接包裹要渲染的组件。唯一的方法是访问RouterView 渲染的组件和传递给组件的props。

详细设计

我们可以通过slot来实现:

vue

<router-view v-slot="{ Component, route }">
<component :is="Component" v-bind="route.params"></component>
</router-view>

在这个例子中,Component是一个可以传递给渲染函数h或者props is要求的组件。

当在route定义中定义了props: true

js
createRouter({
    routes: [{path: '/users/:id', component: User, props: true}],
})

router-view会自动将params中的id属性的值作为props传递给要渲染的组件。注意你也可不添加props: true 的声明,通过v-bind="route.params"传递给组件。

没有匹配到任何路由的用例

在当前的路由没有匹配到在router注册的任何record时,RouteLocationmatched数组将会是空的,而且router-view 并没有并没有为默认slot提供默认内容,所以不会渲染任何东西。当提供一个未匹配路由时的slot时,我们可以决定展示什么,是要显示Not Found的页面还是要显示默认行为,我们都可以做到。Component值为falsy时,不会展示任何组件:

vue

<template>
  <router-view v-slot="{ Component, route }">
    <component v-if="route.matched.length > 0" :is="Component"/>
    <div v-else>Not Found</div>
  </router-view>
</template>

注意这与在路由表中定义捕获所有路由path: '/:pathMatch(.*)来展示Not Found页面的行为是重复的。

WARNING

在router-view中如此使用,没有在路由表中定义path: '/:pathMatch(.*)更灵活。当使用前者时,由于在路由表中找不到要挂载的组件,会将整体替换v-else中的内容。 但是使用后者时,可以灵活改变Not Found组件渲染的位置。

使用前者时

img.png

使用后者时

img.png

v-slot属性

  • Component:可以传递给渲染函数h或者props is要求的组件。
  • route:被RouterView渲染的标准路由RouteLocationNormalized。与$route相同,但是但允许在 JSX 中轻松进行类型化访问。

使用Transition或者KeepAlive包裹RouterView

如果有用户意外使用Transition包裹RouterView 或者迁移项目到Vue3,我们可以抛出警告信息引导用户阅读文档并且暗示他们使用v-slot

同时使用Transition或者KeepAlive

当同时使用Transition或者KeepAlive时,我们需要先使用Transition,后使用KeepAlive

vue

<template>
  <RouterView v-slot="{Component}">
    <Transition>
      <KeepAlive>
        <component :is="Component"></component>
      </KeepAlive>
    </Transition>
  </RouterView>
</template>

可替代方案

  • 使用一个类似useView的函数,返回Componentattrs属性,并移除RouteViewv-slot的用法

采取的策略

  • 将vue-router3的代码模板基于vue-router4重写。