基于高阶组件(HOC)与递归链路缓存的多级路由缓存方案

1. 核心问题背景(痛点)

  • Vue 层级限制:Vue 的 <keep-alive> 只能缓存其直接子组件。在 Layout > 目录 > 页面 的三级嵌套结构中,三级页面(孙子组件)由于被二级目录(父组件)包裹,无法直接被 Layout 层级的缓存器识别。

  • 中间容器失效:如果中间层目录只是一个简单的 RouterView,它在子路由切换时会销毁并重建内部组件,导致缓存链路断裂。

  • 命名冲突问题:项目中大量使用 index.vue 导致自动推导的组件名均为 index,使 keep-alive 的 include 匹配逻辑失效。

2. 核心方案设计

方案采用了 “动态包装 + 递归缓存” 的思路,彻底解决了多级嵌套下的状态保持问题:

在路由解析阶段(createRouteRecord),不直接挂载原始 .vue 组件,而是通过一个自定义的 createDynamicComponent 函数进行包装:

  • 强制命名:通过 defineComponent 动态赋予组件唯一的 name(格式如 App-${paths}),确保与 keep-alive 的 include 列表精确匹配,解决了 index.vue 的重名陷阱。

  • 规避 HTML 冲突:为组件名添加 App- 前缀,避开了如 <article>、<section> 等 HTML5 保留标签导致的渲染警告。

  • 异步兼容:内部集成 defineAsyncComponent 处理懒加载组件,解决了直接使用渲染函数时可能出现的 [object Promise] 渲染错误。

在动态包装函数中,对 目录型路由(CATALOGUE) 进行特殊处理:

  • 自承载缓存:为目录型组件内部注入一套独立的 <KeepAlive> 逻辑。

  • 缓存传递:目录组件会监听全局 Pinia Store 中的缓存列表,并将缓存状态向下传递。

  • 结果:形成“Layout 缓存目录,目录缓存页面”的逐级锁定机制,确保整条路由链路上的组件在内存中均不被销毁。

  • 链路提取:修改 Tab 标签页的添加逻辑,通过 route.matched 递归提取当前路由的所有父级目录的 cacheName。

  • 批量缓存:当用户打开一个三级页面时,自动将“一级目录、二级目录、三级页面”的组件名同时推入 cacheTabList。

  • 动态 Key 追踪:在嵌套渲染函数中显式绑定 key: route.fullPath,解决了 Vue 在多级嵌套下首次加载页面可能出现的“组件已加载但页面空白”的渲染异常问题。

  • 非响应式处理:使用 markRaw 包装动态组件定义,避免 Vue 对复杂组件对象进行深度响应式代理,提升了大数据量场景下的系统性能。

3. 方案成果

  • 结构保留:无需为了缓存而扁平化路由,URL 物理路径与面包屑层级依然维持树状结构。

  • 零侵入性:无需手动修改成百上千个具体的 .vue 业务文件,所有逻辑在底层路由配置中自动完成。

  • 跳转稳定:将路由 name 由 Symbol 改为唯一字符串拼接(paths_id),彻底解决了权限导航时可能出现的登录不跳转问题。

posted @ 2026-01-12 16:37  Life_countdown  阅读(22)  评论(0)    收藏  举报