🧩 Vue Router嵌套路由新范式:无需嵌套 RouterView 的布局实践

引言

在上一篇文章《🧩 vue-router 动态页缓存异常处理方案解析》中,我们探讨了 Vue Router 动态页缓存的异常处理方案。如今,vue-router-better-view 又新增一项关键特性扁平化嵌套路由渲染。本文将深入解析该特性的实现原理及应用场景。

背景问题

在中后台开发中,列表页与表单页的联动是高频场景。例如:

  • 用户列表页点击项跳转详情页
  • 商品列表页进入编辑页

此时我们可能会编写出如下的 routes

ts 复制代码
// src/router/index.ts
const routes: RouteRecordRaw[] = [
  {
    path: '/system',
    component: Layout, // 布局组件
    children: [
      {
        path: 'user',
        component: UserList, // 用户列表组件
        children: [
          {
            path: 'detail/:id?',
            component: UserDetail, // 用户详情组件
          },
        ],
      },
    ],
  },
]

此时当我们信心满满的开始访问 /system/user/detail 时却会发现页面一直处于 UserList 组件中,why?

可能有人会说官方给出的嵌套路由方案需要同时使用嵌套 RouterView,比如在 UserList 中增加子页面渲染的 RouterView 逻辑或者将子页面与列表页同级注册到布局组件中,其中同级注册应该是我们最常用的解决方案了,但是同样存在以下问题:

方案类型 优点 缺点
嵌套 RouterView 保持路由层级关系 需手动嵌套多个 <RouterView>,代码冗余
同级注册 简化组件层级 丢失 route.matched 的路由匹配信息,而且定义也不符合直觉

本文将深入探讨如何通过 Vue Router 的 viewDepth 机制官方导出的内部 key ,实现 扁平化嵌套路由渲染 ,让上面符合开发直觉的嵌套路由渲染不再是问题。

问题解决:如何实现精准渲染?

RouterView 的渲染机制分析

为了方便观看删除了一些无关代码!

ts 复制代码
const RouterViewImpl = defineComponent({
  setup(props, { attrs, slots }) {
    const injectedRoute = inject(routerViewLocationKey);
    const routeToDisplay = computed(() => props.route || injectedRoute.value);
    // 重点:注入视图深度
    const injectedDepth = inject(viewDepthKey, 0);

    // 计算当前深度,跳过无组件的父路由
    const depth = computed(() => {
      let initialDepth = unref(injectedDepth);
      const { matched } = routeToDisplay.value;
      let matchedRoute;
      // 这里就是为什么官方仅支持不携带组件的父级路由
      while ((matchedRoute = matched[initialDepth]) && !matchedRoute.components) {
        initialDepth++;
      }
      return initialDepth;
    });

    // 提供当前深度 +1 给下一个嵌套的 RouterView
    provide(viewDepthKey, computed(() => depth.value + 1));
    
    ...
  },
});

上面代码介绍了 RouterView 是如何进行视图组件渲染的,里面的 viewDepthKey 起到了关键作用,我们再看看 viewDepthKey 的介绍。

ts 复制代码
/**
 * Allows overriding the router view depth to control which component in
 * `matched` is rendered. rvd stands for Router View Depth
 *
 * @internal
 */
const viewDepthKey = Symbol('router view depth' );

上面的注释通过翻译后大概意思是:允许覆盖路由视图的深度,以控制 matched 中的哪个组件会被渲染。

接下来思路就比较明确了,可以通过导出的 viewDepthKeyRouterView 内部渲染的视图深度进行精准控制。

实现精准渲染

html 复制代码
<script setup lang="ts">
import { provide, computed, inject } from 'vue'
import { viewDepthKey, useRoute } from 'vue-router'

const injectedViewDepth = inject(viewDepthKey, 0)
const route = useRoute()

// 直接提供当前路由最后一个匹配项的视图索引
provide(viewDepthKey, computed(() => route.matched.length - 1))
</script>

<template>
  <main>
    <RouterView />
  </main>
</template>

通过库实现

html 复制代码
<script setup lang="ts">
import { BetterRouterView, useExactView } from 'vue-router-better-view'

// 若不想使用 BetterRouterView 时,也可通过提供的 useExactView 函数进行实现
// useExactView({ exact: true })
</script>

<template>
  <main>
    <BetterRouterView exact />
    
    <!-- 在使用 useExactView 时可以用原生的 RouterView -->
    <!-- <RouterView /> -->
  </main>
</template>

结语

通过 viewDepthKey 机制,我们可以突破 Vue Router 原生嵌套路由的限制,实现更灵活的扁平化渲染。vue-router-better-view 将这一能力封装为开箱即用的组件,显著提升了代码简洁性与可维护性。

相关链接

相关推荐
用户47949283569158 分钟前
记住这张时间线图,你再也不会乱用 useEffect / useLayoutEffect
前端·react.js
汝生淮南吾在北11 分钟前
SpringBoot+Vue养老院管理系统
vue.js·spring boot·后端·毕业设计·毕设
咬人喵喵20 分钟前
14 类圣诞核心 SVG 交互方案拆解(附案例 + 资源)
开发语言·前端·javascript
问君能有几多愁~33 分钟前
C++ 日志实现
java·前端·c++
咬人喵喵35 分钟前
CSS 盒子模型:万物皆是盒子
前端·css
2401_8603195241 分钟前
DevUI组件库实战:从入门到企业级应用的深度探索,如何快速应用各种组件
前端·前端框架
韩曙亮1 小时前
【Web APIs】元素滚动 scroll 系列属性 ② ( 右侧固定侧边栏 )
前端·javascript·bom·window·web apis·pageyoffset
珑墨1 小时前
【浏览器】页面加载原理详解
前端·javascript·c++·node.js·edge浏览器
LYFlied2 小时前
在AI时代,前端开发者如何构建全栈开发视野与核心竞争力
前端·人工智能·后端·ai·全栈
用户47949283569152 小时前
我只是给Typescript提个 typo PR,为什么还要签协议?
前端·后端·开源