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

相关链接

相关推荐
Zestia10 分钟前
页面点击跳转源代码?——element-jumper插件实现
前端·javascript
前端小白199510 分钟前
面试取经:工程化篇-webpack性能优化之优化loader性能
前端·面试·前端工程化
PineappleCoder10 分钟前
大小写 + 标点全搞定!JS 如何精准统计单词频率?
前端·javascript·算法
zhangbao90s11 分钟前
Web组件:使用Shadow DOM
前端
hhy前端之旅12 分钟前
语义版本控制:掌握版本管理的艺术
前端
coding随想12 分钟前
深入浅出DOM操作的隐藏利器:Range(范围)对象——掌控文档的“手术刀”
前端
前端小白199512 分钟前
面试取经:工程化篇-webpack性能优化之减少模块解析
前端·面试·前端工程化
一枚前端小能手13 分钟前
🏗️ 项目越来越大维护不动了,微前端架构了解一下
前端
文艺理科生21 分钟前
Nuxt.js入门指南-Vue生态下的高效渲染技术
前端·vue.js·nuxt.js
夏小花花25 分钟前
vue3 ref和reactive的区别和使用场景
前端·javascript·vue.js·typescript