You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue+Express SSR因Hydration不匹配失败,求原因排查

排查Vue SSR Hydration不匹配问题(本地打包Vue替代CDN引入)

可能原因及解决方案

1. 打包模式未区分SSR/客户端环境

Vite默认打包是面向客户端的,若未为SSR场景单独配置,产物会缺失SSR适配逻辑,导致服务端与客户端渲染逻辑不一致:

  • 新增SSR专属打包配置:
    // vite.ssr.config.js
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    
    export default defineConfig({
      plugins: [vue()],
      build: {
        ssr: true,
        outDir: 'dist-ssr',
        rollupOptions: {
          input: './src/entry-server.js',
          output: { format: 'cjs' } // SSR需CommonJS格式
        }
      }
    })
    
  • 客户端打包单独配置,避免混入SSR代码:
    // vite.client.config.js
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    
    export default defineConfig({
      plugins: [vue()],
      build: {
        outDir: 'dist-client',
        rollupOptions: {
          input: './src/entry-client.js'
        }
      }
    })
    
  • 执行打包时分别调用对应配置:vite build --config vite.ssr.config.js 和 vite build --config vite.client.config.js

2. Vue版本不一致

本地打包的Vue版本与原CDN版本存在差异,会导致渲染输出的DOM结构细节不同:

  • 查看原CDN引入的Vue版本(如https://unpkg.com/vue@3.3.4/dist/vue.global.js),在本地项目中锁定相同版本:
    npm install vue@3.3.4 --save-exact
    

3. 依赖客户端环境的代码提前执行

服务端渲染时不存在window、document等浏览器API,若组件在setup阶段直接调用这些API,会导致服务端输出空值/占位符,客户端渲染时生成实际内容,引发结构不匹配:

  • 错误示例:
    <template>
      <div>{{ window.innerWidth }}</div>
    </template>
    
  • 修正为客户端挂载后再获取:
    <script setup>
    import { ref, onMounted } from 'vue'
    const width = ref(0)
    onMounted(() => {
      width.value = window.innerWidth
    })
    </script>
    <template>
      <div>{{ width }}</div>
    </template>
    

4. 产物引入路径错误

客户端若误引入SSR打包产物,会导致运行逻辑与服务端渲染的Vue实例不兼容:

  • 确保HTML中引入的是客户端打包产物:
    <!-- 正确 -->
    <script src="/dist-client/index.js"></script>
    <!-- 错误 -->
    <script src="/dist-ssr/entry-server.js"></script>
    

5. 服务端状态序列化异常

服务端注入到HTML的初始化状态未正确序列化,客户端无法恢复一致状态,导致渲染差异:

  • 服务端需将状态序列化为JSON字符串:
    // 服务端渲染逻辑
    const app = createSSRApp(App)
    const renderedHtml = await renderToString(app)
    const initialState = JSON.stringify(app._instance.setupState)
    return `
    <html>
      <body>
        <div id="app">${renderedHtml}</div>
        <script>window.__INITIAL_STATE__ = ${initialState}</script>
        <script src="/dist-client/index.js"></script>
      </body>
    </html>
    `
    
  • 客户端读取并恢复状态:
    // 客户端入口
    const app = createSSRApp(App)
    if (window.__INITIAL_STATE__) {
      app._instance.setupState = window.__INITIAL_STATE__
    }
    app.mount('#app')
    

快速验证步骤

  1. 用浏览器开发者工具对比服务端输出的原始HTML和客户端挂载后的DOM结构,高亮的差异节点就是问题根源
  2. 临时换回CDN引入的Vue,确认错误消失,排除业务代码本身的问题
  3. 查看Vite打包日志,检查是否有未处理的警告(如未兼容SSR的依赖)

内容的提问来源于stack exchange,提问作者Yang Jiang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:50:28