Vue3 SSR场景下keep-alive包裹动态组件水合不匹配如何修复?
错误原因
水合不匹配由两个核心问题导致:
- 错误使用
router-view插槽参数:Vue Router的router-view默认插槽仅传递Component(待渲染的路由组件)、route(当前路由对象)两个属性,你解构的index是不存在的插槽属性,服务端渲染时拿到的值为undefined,导致动态组件渲染为空注释;而你在setup中又返回了同名的index变量,两端变量作用域优先级差异导致渲染的节点结构不一致。 - Vue3 SSR 中
<keep-alive>服务端渲染会输出特殊的片段注释标记,错误的组件绑定逻辑导致客户端生成的vnode节点结构和服务端输出的DOM结构无法对齐,触发水合警告。
修复步骤
1. 修正router-view插槽使用逻辑
直接使用插槽传递的Component作为动态组件的渲染源,不需要硬编码固定组件,也不需要在setup中额外引入路由对象,插槽已经传递了当前路由实例。
2. 调整keep-alive配置
给keep-alive添加include属性匹配要缓存的组件名称,所有需要缓存的页面组件必须显式声明name属性,否则无法被识别。
修复后完整代码
<template> <div class="default-layout"> <router-view v-slot="{ Component, route }"> <keep-alive :include="cachedComponents"> <component :is="Component" :key="route.fullPath" /> </keep-alive> </router-view> </div> </template> <script lang="ts"> import { defineComponent, ref } from "vue"; export default defineComponent({ name: "App", setup() { // 填入所有需要缓存的页面组件的name值 const cachedComponents = ref(['Index', 'OtherPage']) return { cachedComponents } } }); </script>
兼容方案
如果修改后仍然存在水合警告,可以添加客户端渲染判断,首屏服务端渲染时不渲染keep-alive,客户端挂载完成后再启用缓存,完全规避结构不匹配问题:
<template> <div class="default-layout"> <router-view v-slot="{ Component, route }"> <keep-alive :include="cachedComponents" v-if="isClient"> <component :is="Component" :key="route.fullPath" /> </keep-alive> <component :is="Component" :key="route.fullPath" v-else /> </router-view> </div> </template> <script lang="ts"> import { defineComponent, ref, onMounted } from "vue"; export default defineComponent({ name: "App", setup() { const cachedComponents = ref(['Index', 'OtherPage']) const isClient = ref(false) onMounted(() => { isClient.value = true }) return { cachedComponents, isClient } } }); </script>
该方案的缺点是首屏页面不会进入缓存,路由切换后缓存才会生效,对首屏性能无影响。
内容的提问来源于stack exchange,提问作者Ziwen Wang
相关产品推荐
相关产品推荐

