🧩 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 将这一能力封装为开箱即用的组件,显著提升了代码简洁性与可维护性。

相关链接

相关推荐
Csvn7 小时前
OpenSpec 详细使用教程
前端
之歆8 小时前
Day19_LESS 完全指南——从入门到工程实践
前端·css·less
云水一下9 小时前
HTML5 从入门到精通:实战收官——从零搭建完整静态网站,综合运用所有知识
前端·html5
不总是9 小时前
Windows 系统 Node.js 免安装版(zip)安装与配置教程(2026 最新)
前端·windows·node.js
冬奇Lab9 小时前
每日一个开源项目(第105篇):Twenty - 跳出 Salesforce 的圈套,定义现代开源 CRM
前端·后端·开源
zhangyao94033010 小时前
开发pc端时,表格的高度怎么设置才能铺满页面
前端·javascript·elementui
kjs--11 小时前
浏览器书签执行脚本
前端
之歆11 小时前
Day16_JavaScript 轮播图与事件工程实战(下篇)
服务器·开发语言·前端·javascript·网络·性能优化
沄媪11 小时前
CSRF 跨站请求伪造
前端·ctf·csrf
kyriewen11 小时前
我关掉了Copilot:因为我写的代码出现在了别人的建议里
前端·javascript·ai编程