keep-alive 的 include 传了路由 name:Vue3 动态路由生产环境白屏排查
项目基于 Vue3 + Vue Router + 动态路由。开发环境一切正常,打包部署后路由切换白屏、控制台无报错、刷新页面偶发恢复。最终定位到 keep-alive 的 include 用错了值。
核心结论
keep-alive 的 include 只能匹配组件的 name,不能使用路由 name。这种写法在开发环境可能看起来正常,但在「生产环境 + 动态路由 + 异步组件」的组合下会直接阻断渲染,表现为白屏。
问题表现
| 场景 | 结果 |
|---|---|
| 本地 dev | 正常 |
| build 后部署 | 路由切换白屏 |
| 控制台 | 无报错 |
| 刷新页面 | 偶发恢复 |
没有报错这一点最干扰判断:白屏通常先怀疑路由匹配失败或接口异常,但这里两者都正常。
直接原因:代码层面
guards.ts 里的错误逻辑:
const currentComName =
to.matched.find(item => item.name === to.name)?.name;
对应的渲染模板:
问题点很明确:to.name 是路由 name,而 keep-alive 的 include 匹配的是组件 name,两者不在同一个命名空间里,永远不可能匹配上。include 匹配失败时,被包裹的组件不会进入渲染流程。
为什么只在生产环境出问题
- 开发环境:组件同步加载,HMR 会强制刷新,
keep-alive的行为相对宽松,匹配失败也常常看不出来。 - 生产环境:组件走异步
import,chunk 被拆分,组件 name 严格生效;include匹配失败后组件不渲染。
开发环境在这里起的是掩盖作用,不是验证作用。
根本原因
路由 name ≠ 组件 name,keep-alive 只认组件 name。链路是:
include 不匹配 → 缓存失败 → 渲染被阻断 → 白屏。
解决方案
稳定优先,做了三件事:
- 彻底移除
keep-alive; - 用
route.fullPath作为key强制重新渲染; - 保证动态路由在生产环境可控运行。
关键修改代码
1. Main / index.vue(最关键)
❌ 组件加载失败:{{ route.path }}
修改点:移除 ;移除 ;使用 route.fullPath 作为 key。
2. 路由守卫增加诊断日志(辅助)
const matchedRoute = to.matched.at(-1);
console.log('route matched', {
path: to.path,
hasComponent: matchedRoute?.components?.default,
});
结果对比
| 项 | 修改前 | 修改后 |
|---|---|---|
| 路由切换 | 白屏 | 正常 |
| 页面刷新 | 偶发 | 稳定 |
| keep-alive 异常 | 存在 | 移除 |
| 生产可控性 | 低 | 高 |
经验结论
keep-alive 是组件缓存,不是路由缓存。动态路由场景下误用 include 极易在生产环境出问题。后台系统中稳定性优先于缓存。
后续优化建议
如果确实必须使用 keep-alive,需要先明确所有页面组件的 name,从 to.matched[].components.default.name 获取,或者改用 Pinia 缓存状态,而不是依赖组件实例。
一句话总结:开发环境掩盖问题、生产环境放大问题,这次踩的是 keep-alive 的使用边界。