【问题标题】:Vuejs Error: The client-side rendered virtual DOM tree is not matching server-renderedVuejs 错误:客户端渲染的虚拟 DOM 树与服务器渲染的不匹配
【发布时间】:2021-06-08 21:34:24
【问题描述】:

我在我的应用程序中使用 Nuxt.js / Vuejs,但我在不同的地方一直面临这个错误:

    The client-side rendered virtual DOM tree is not matching server-rendered content. 
This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. 
Bailing hydration and performing full client-side render.

我想了解调试此错误的最佳方法是什么?他们是我可以记录/获取客户端和服务器的虚拟 DOM 树以便我可以比较并找到错误所在的一种方式吗?

我的是一个大型应用程序,手动验证很困难。

【问题讨论】:

标签: javascript vue.js vuejs2 nuxt.js


【解决方案1】:

部分答案:使用 Chrome DevTools,您可以定位问题并准确查看导致问题的因素。执行以下操作(我使用 Nuxt 5.6.0 和 Chrome 64.0.3282.186 执行此操作)

  1. 在 Chrome 中显示 DevTools (F12)
  2. 加载导致“客户端呈现的虚拟 DOM 树...”警告的页面。
  3. 滚动到 DevTools 控制台中的警告。
  4. 单击警告的源位置超链接(在我的例子中是 vue.runtime.esm.js:574)。
  5. 在此处设置断点(左键单击源代码浏览器中的行号)。
  6. 再次显示相同的警告。我并不是说这总是可能的,但就我而言,我只是重新加载了页面。如果有很多警告,您可以通过将鼠标移动到msg 变量上来检查消息。
  7. 当您找到消息并在断点处停止时,请查看调用堆栈。向下单击一帧以调用“补丁”以打开其源代码。将鼠标悬停在 patch 中执行行上方 4 行的 hydrate 函数调用上。指向hydrate 源的超链接将打开。
  8. hydrate函数中,从开头移动大约15行并设置一个断点,在assertNodeMatch返回false之后返回false。在那里设置断点并删除所有其他断点。
  9. 再次发出相同的警告。现在,当断点被命中时,执行应该在hydrate 函数中停止。切换到 DevTools 控制台并评估elm,然后评估vnode。这里 elm 似乎是一个服务器渲染的 DOM 元素,而 vnode 是一个虚拟 DOM 节点。 Elm 打印为 HTML,因此您可以找出错误发生的位置。

【讨论】:

  • Nuxt 5.6.0,你活在未来吗?
  • 访问 hydrate 函数执行的更快捷的方法是在 Chrome 开发工具的控制台区域中展开错误,您可以在列表中看到它。只需单击同一行的@ 符号后的链接。例如水合物@commons.app.js:15934
  • 我发现这个博客根据@budden73 的回答发布了这个错误的扩展解释,它实际上帮助我理解了这个问题。希望这可以帮助其他人:blog.lichter.io/posts/vue-hydration-error
【解决方案2】:

对我来说,发生此错误是因为在AsyncData 中获取数组列表并由v-for 呈现&lt;tr&gt; 标签,我将v-for 代码放入&lt;client-only&gt; 块中并解决了问题

【讨论】:

  • 如果您不使用 Nuxt,则需要安装 vue-client-only
  • 这是否意味着我们不能在服务器端渲染表?并将完整的 html 发送到浏览器?这真的破坏了 Nuxt 的 SSR 和 SEO 功能的概念。我遇到了同样的问题,可以用 块解决,但我猜这不是真正的解决方案
  • @Tekz 您可以在服务器端渲染表,只要您确保行包含在 &lt;thead&gt;&lt;tbody&gt;&lt;tfoot&gt; 标签中(有关如何使用这些标签的参考,请参阅 MDN标签正确)
  • @FelixEve 这个不再需要了,因为它已经融入 Nuxt。
【解决方案3】:

这个错误调试起来真的很痛苦。为了快速获取导致问题的元素,请编辑 node_modules/vue/dist/vue.esm.js 并添加以下行:

// Search for this line: 
function hydrate (elm, vnode, insertedVnodeQueue, inVPre) {
    var i;
    var tag = vnode.tag;
    var data = vnode.data;
    var children = vnode.children;
    inVPre = inVPre || (data && data.pre);
    vnode.elm = elm;

    // Add the following lines: 
    console.log('elm', elm)
    console.log('vnode', vnode)
    console.log('inVpre', inVPre)
    // ...


您将在控制台中看到失败的节点。

【讨论】:

    【解决方案4】:

    在实施vue-particles 包时,我遇到了与nuxt 版本2.14.0 相同的问题。解决方法是用no-ssr 包围标签并解决了问题。

    编辑:
    解决方案的更新变体(如果 Nuxt 版本高于 2.9.0

    <client-only>
      <vue-particles>
      </vue-particles>
    </client-only>
    

    旧解决方案:

    <no-ssr>
      <vue-particles>
      </vue-particles>
    </no-ssr>
    

    【讨论】:

      【解决方案5】:

      有很多方法可以解决这个问题,但大多数都不是真正的解决方法,只是简单的创可贴。注意几点:

      • 将其包装到&lt;client-only&gt; 标签中,注意一些important details tho
      • 使用v-show 而不是v-if
      • 试图破解一些生命周期
      • 等等……

      我强烈推荐阅读这篇由 Alexander Lichter 撰写的精彩文章

      https://blog.lichter.io/posts/vue-hydration-error/

      他会向您解释,您应该诊断为什么会发生这种情况并解决实际问题。
      基本上每次与在服务器上生成的内容不同以及在客户端完成补水时可用的内容都会导致此错误。

      其中一些是:

      • 无效的 HTML(在 &lt;p&gt; 内有一个块元素,同样适用于嵌套在另一个标签中的 a 标记,等等...)
      • 第三方脚本会影响您的组件
      • 服务器与客户端的不同状态
      • 任何随机都是有风险的(例如new Date()
      • 任何与身份验证相关的页面

      我强烈建议您阅读这篇文章,用 Alexandre 自己的话来了解如何处理此类问题。如果您赶时间,您可以随时使用创可贴修复,但尝试实际修复问题以获得最佳性能并保持代码干净。

      【讨论】:

        【解决方案6】:

        对于 2.10 以上的 Nuxt 版本,不需要安装任何东西,只需使用 https://nuxtjs.org/api/components-client-only/ 提到的默认组件 &lt;client-only&gt;

        【讨论】:

          【解决方案7】:

          查看之前的警告:

          "nuxt": "^2.12.2",您可以从之前的警告中轻松发现原因。

          就我而言:

          不正确

          <nuxt-link to="/game42day">
            <a>Game For Today</a>
          </nuxt-link>
          

          正确:

          <nuxt-link to="/game42day">
            Game For Today
          </nuxt-link>
          

          【讨论】:

            【解决方案8】:

            事实证明,在我的情况下,我有 HTML 注释标签,这导致了这个愚蠢而烦人的错误。我花了很长时间才弄明白,但万一它对某人有帮助。

            【讨论】:

              【解决方案9】:

              如果您使用v-if 有条件地渲染组件,那么您有两种选择来解决问题:

              第一个是将元素包裹在&lt;no-ssr&gt;&lt;/no-ssr&gt;标签中。

              第二种方法是将v-if替换为v-showhere是Vue文档的链接。

              【讨论】:

                【解决方案10】:

                在我的情况下,我不得不改变这个:

                <v-expansion-panel-header v-text="name" />
                

                到这里:

                <v-expansion-panel-header>{{ name }}</v-expansion-panel-header>
                

                【讨论】:

                  【解决方案11】:

                  好吧,这听起来很傻。我在大约 15 分钟内尝试了一堆不同的解决方案,例如重新启动服务器和删除 .nuxt 目录,但我懒得使用@budden73 的大脑解决方案。最终对我有用的只是重新启动我的计算机,试一试。

                  【讨论】:

                    【解决方案12】:

                    到目前为止,我从观察中发现,当您使用第三方包(如 jQuery(特别是))时,它们有时会将 html 标签注入到 dom 中。因此 Vue/Nuxt 失去了对 dom 树的跟踪并开始抱怨。

                    我遇到了同样的问题,过了一段时间我删除了所有 jQuery 并用 Vuejs 替换了 jQuery 功能,这些错误都消失了。

                    【讨论】:

                      【解决方案13】:

                      有关如何处理修改 DOM 的集成(例如 Google Analytics 或 FB Pixel)的示例,请参见此处。基本上是创建一个插件并从 SSR 中排除。

                      https://nuxtjs.org/faq/ga

                      【讨论】:

                        【解决方案14】:

                        怎么样:

                        extend (config, ctx) {
                          config.resolve.symlinks = false
                        }
                        

                        看到这个[Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content ( Nuxt / Vue / lerna monorepo )

                        【讨论】:

                          【解决方案15】:

                          由于这个问题,我也遇到了很多错误。列举两个我经常遇到的案例,希望能帮到你。

                          • 使用 vuetify 按钮,当你创建一个通用组件时,你应该使用:&lt;v-btn&gt;{{text}}&lt;/v-btn&gt;。示例:
                          <template>
                              <v-btn
                                :width="width"
                                :color="color"
                                :class="[rounded ? 'rounded-pill' : 'rounded-lg',textColor]"
                                v-on:click="onClick"
                                elevation="0"
                                :outlined="outlined"
                                :type="type"
                                :name="name"
                                :form="form"
                                :disabled="disabled"
                                v-bind="$attrs"
                              >{{ text }}</v-btn>
                          </template>
                          
                          • 不要使用带有&lt;p&gt; 标签的v-html。 不使用:&lt;p v-html='html'&gt;&lt;/p&gt;。 使用:&lt;div v-html='html'&gt;&lt;/div&gt;

                          另外,如果你使用&lt;client-only&gt;&lt;/client-only&gt;,这个问题肯定解决了,但是如果你需要SEO页面或者展示google ads,那就不是很好的解决方案了。

                          【讨论】:

                            【解决方案16】:

                            检查是否在内联元素中使用了任何块级元素。

                            例如:里面,里面

                            如果您使用过 HTML 表格,请确保您使用过标签

                            【讨论】:

                              【解决方案17】:

                              就我而言,我将代码从

                              <p v-html="$md.render(post.content)"></p>
                              

                              <p>{{ $md.render(post.content) }}</p>
                              

                              【讨论】:

                                猜你喜欢
                                • 2021-05-27
                                • 2020-02-03
                                • 1970-01-01
                                • 2021-11-02
                                • 2020-05-13
                                • 2020-04-12
                                相关资源
                                最近更新 更多