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

相关链接

相关推荐
带娃的IT创业者10 小时前
TypeScript + React + Ant Design 前端架构入门:搭建一个 Flask 个人博客前端
前端·react.js·typescript
非凡ghost11 小时前
MPC-BE视频播放器(强大视频播放器) 中文绿色版
前端·windows·音视频·软件需求
Stanford_110611 小时前
React前端框架有哪些?
前端·微信小程序·前端框架·微信公众平台·twitter·微信开放平台
洛可可白12 小时前
把 Vue2 项目“黑盒”嵌进 Vue3:qiankun 微前端实战笔记
前端·vue.js·笔记
学习同学12 小时前
从0到1制作一个go语言游戏服务器(二)web服务搭建
服务器·前端·golang
-D调定义之崽崽13 小时前
【初学】调试 MCP Server
前端·mcp
四月_h13 小时前
vue2动态实现多Y轴echarts图表,及节点点击事件
前端·javascript·vue.js·echarts
文心快码BaiduComate13 小时前
用Zulu轻松搭建国庆旅行4行诗网站
前端·javascript·后端
正义的大古14 小时前
OpenLayers地图交互 -- 章节十八:拖拽旋转和缩放交互详解
javascript·vue.js·openlayers
行者..................15 小时前
手动编译 OpenCV 4.1.0 源码,生成 ARM64 动态库 (.so),然后在 Petalinux 中打包使用。
前端·webpack·node.js