基于高阶组件(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),彻底解决了权限导航时可能出现的登录不跳转问题。

浙公网安备 33010602011771号