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

相关链接

相关推荐
拾光拾趣录11 分钟前
for..in 和 Object.keys 的区别:从“遍历对象属性的坑”说起
前端·javascript
OpenTiny社区22 分钟前
把 SearchBox 塞进项目,搜索转化率怒涨 400%?
前端·vue.js·github
编程猪猪侠1 小时前
Tailwind CSS 自定义工具类与主题配置指南
前端·css
qhd吴飞1 小时前
mybatis 差异更新法
java·前端·mybatis
YGY Webgis糕手之路1 小时前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
患得患失9491 小时前
【前端】【vueDevTools】使用 vueDevTools 插件并修改默认打开编辑器
前端·编辑器
ReturnTrue8681 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
UncleKyrie1 小时前
一个浏览器插件帮你查看Figma设计稿代码图片和转码
前端
遂心_1 小时前
深入解析前后端分离中的 /api 设计:从路由到代理的完整指南
前端·javascript·api
你听得到112 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构