router-view组件是做什么的
router-view 是 Vue Router 的核心组件,就是根据当前路由路径动态渲染匹配的组件。
我们写一个先写一个router的配置。
import { createRouter, createWebHistory } from '../mini-router'
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'
import AView from '../views/AView.vue'
import BView from '../views/BView.vue'
import CView from '../views/CView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
children: [
{
path: 'a',
component: AView
},
{
path: 'b',
component: BView
},
{
path: 'c',
component: CView
}
]
},
{
path: '/about',
name: 'about',
component: AboutView
}
]
})
export default router
假设目前路径为: /a
<!-- 父组件模板 -->
<template>
<!-- 深度0:渲染根路径(/)的组件 -->
<router-view> <!-- 渲染 HomeView -->
<!-- 深度1:渲染嵌套路由(/a)的组件 -->
<router-view/> <!-- 渲染 AView -->
</router-view>
</template>
那么第一个router-view组件就加载 /路径的组件
第二个router-view组件就加载/a 路径对应的组件
把这件事件请说的在公式一些就是
/a路径可以匹配到两个组件 / 和 /a,写成数组就是
// 路径 /a 的匹配结果
[
{ path: '/', component: HomeView }, // 深度0
{ path: '/a', component: AView } // 深度1
]
所以第一个router-view的深度为0,第二个router-view的深度为1,如果还有嵌套那么第三个router-view的深度就为3。
然后每个router-view组件的深度就是他需要加载的组件在数组中的下标
所以我们要做到根据路径加载对应组,我们就需要完成以下几件事情。
- 动态渲染某个组件
- 根据路径获取到这样一个数组
// 路径 /a 的匹配结果 [ { path: '/', component: HomeView }, // 深度0 { path: '/a', component: AView } // 深度1 ] - 每个router-view都需确定其深度。
确定了这三个条件之后,就可以根据router-view的深度,去匹配的数组中找到对应组件进行渲染即可。
思路有了,我们就开实现每个条件。
- 动态渲染,可以使用component组件或者h函数返回虚拟dom,这里就使用h函数了。
- 匹配这里就直接使用内置的方法了。
- 对于深度,我们只需要知道父组件的深度,然后 + 1就是自己组件的深度,肯定没办法使用prop传递给子组件自己的深度,因为我们没有办法直接找到对应的子router-view组件,但是router-view既然是自己的子组件,所以我们可以使用
provide传值。
具体实现
import { inject, computed, type Slots, provide, h } from 'vue'
import type { RouteLocationNormalizedLoaded } from 'vue-router'
export const RouterView = {
name: 'RouterView',
setup(_: unknown, { slots }: { slots: Slots }) {
// 当我们进行组件渲染的时候,这里会存在一个层级的关系
// 因为 matched 是一个有层次关系(父子关系)的组件数组,意味着 router-view 可能存在好几个
// 所以这里存在一个“深度”的问题
// 这里可以通过 inject 的方法,从父组件那里去获取 depth
// 根据 depth 来得知当前的 router-view 是第几层
// 如果父组件没有提供 depth,说明这是最顶层的 router-view,我们给一个默认值 0
const depth: number = inject('depth', 0)
const injectRoute: RouteLocationNormalizedLoaded = inject('route location')!
// 获取对应层数的匹配上的路由记录
const matchedRouteRef = computed(() => injectRoute.matched[depth])
// 第一次执行完成后,我们要使其 +1
// 把这些信息提供给下一层
provide('depth', depth + 1)
// 返回一个渲染函数,这个函数在每次重新渲染的时候会被调用
return () => {
// 获取对应的路由记录
const matchRoute = matchedRouteRef.value
// 从路由记录上面获取到组件
const viewComponent = matchRoute && matchRoute?.components?.default
// 查看组件是否存在
if (!viewComponent) {
// 进入到此 if,说明没有找到对应的组件
// 那么就渲染默认的插槽(如果有的话)
return slots.default && slots.default()
}
// 找到了对应的组件
// 渲染该组件
return h(viewComponent)
}
}
}