Vuejs错误:客户端渲染的虚拟DOM树与服务器渲染不匹配。

93
我在我的应用程序中使用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树,以便我可以比较并查找错误所在的位置?

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


6
检查此问题:https://blog.lichter.io/posts/vue-hydration-error/#what-is-vue-hydration%3FVue hydration是什么? - poitroae
21个回答

234
部分答案:使用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形式打印,因此您可以找出错误发生的位置。

79
Nuxt 5.6.0,你是来自未来的吗? - Tim
9
更快地执行hydrate函数的方法是扩展Chrome开发工具的控制台区域中的错误,并在列表中查看它。只需点击同一行中@符号后面的链接即可,例如:hydrate @ commons.app.js:15934。 - Michael Giovanni Pumo
6
我发现这篇博客发布了一个基于@budden73答案的扩展解释,它实际上帮助我理解了这个问题。希望这能帮助其他人:https://blog.lichter.io/posts/vue-hydration-error/ - Alessio
感谢@Alessio链接我的帖子。 - manniL

31

对我来说,这个错误是因为在AsyncData中获取了数组列表,并通过v-for呈现了<tr>标签。我将v-for代码放入了<client-only>块中,问题就解决了。


3
如果您没有使用Nuxt,您需要安装vue-client-only - Felix Eve
1
这是否意味着我们无法在服务器端渲染表格并将完整的HTML发送到浏览器?这真的破坏了Nuxt的SSR和SEO功能的概念。我遇到了同样的问题,可以通过<client-only>块解决,但这不是真正的解决方法。 - Tekz
1
@Tekz,只要确保行被包裹在<thead><tbody><tfoot>标签中(参考MDN如何正确使用这些标签),就可以在服务器端呈现表格。 - GnxR
@FelixEve 这个不再需要了,因为它已经被整合到 Nuxt 中了。 - kissu

25

这个错误可能很难调试。为了快速找到导致问题的元素,请编辑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)
    // ...


你将在控制台中获得失败的节点。


这是调试此问题的最简单方法。可以找出导致任何警告或错误的元素。非常感谢。 - Ashraful Islam
谢谢你的回答,除了编辑这个文件和启动nuxt(可能是yarn dev),你还执行了其他操作吗?对我来说不起作用,根据Chrome中的Sources,仍然使用旧文件。 - Bogdan Timofeev
1
@BogdanTimofeev 我没有执行任何其他操作。我是在不使用Nuxt的情况下使用Vue。对于Nuxt,可能会有另一个类似的文件。 - Dan

22

有许多方法可以解决这个问题,但大部分都不是真正的修复方法,只是 hacky band-aids。以下是其中一些注释:

  • 将其包装在 <client-only> 标签中,但要注意某些重要细节 (链接)
  • 使用 v-show 而不是 v-if
  • 尝试修改某些生命周期
  • 等等...

我强烈推荐阅读 Alexander Lichter 写的这篇精美文章:

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

他会向您解释,应该诊断为什么会发生这种情况并修复实际问题。
基本上,每当某些内容与在服务器上生成的内容不同时,在客户端进行水化时都会导致此错误。

一些原因可能是:

  • 无效的 HTML(例如,块元素放在 <p> 中,或者将一个 a 标签嵌套到另一个标签内等等)
  • 第三方脚本干扰了您的组件
  • 服务器和客户端上的状态不同
  • 任何随机操作都很危险(例如 new Date()
  • 与身份验证相关的任何页面

我强烈建议阅读这篇文章,以了解Alexandre本人如何处理这种问题。如果你很着急,可以使用一种临时性的解决方法,但尽量修复问题以获得最佳性能并保持代码的清洁。


13

在实现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>

12

感谢budden73的回答,我在调试过程中做了一些改进。

  1. 打开开发者工具
  2. 点击 warn 消息,然后点击警告消息的第一行,你将被重定向到名为 vue.runtime.esm.js?xxxx 的文件的 Sources 面板。

enter image description here

  1. ctrl+f 查找上述文件中的 assertNodeMatch,不是函数,而是类似于:
    if (process.env.NODE_ENV !== 'production') {
      if (!assertNodeMatch(elm, vnode, inVPre)) {
        return false
      }
    }
  1. return false这一行设置一个断点。

enter image description here

  1. 刷新页面,断点将会被触发。
  2. Sources面板右侧的Scope->Local下,点击elm元素,您将返回到Elements面板。

enter image description here

  1. 上述元素是客户端渲染的元素,与您的代码进行比较以查看差异。

如果找不到错误来源,修复它的暴力方法是使用nuxt的<client-only>标签。

另一个可能的暴力方法在这里描述。添加一个isHydrate变量,默认为false,在mounted钩子中将其设置为true,并在变量设置为true后呈现元素。


1
很好的建议。对未来是个不错的提示。 - TD1

6

检查之前的警告:

"nuxt": "^2.12.2"中,你可以很容易地从之前的警告中找到原因。

enter image description here

在我的情况下:

不正确

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

正确:

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

6

4

实际上,在我的情况下,我使用了HTML注释标签,这导致了这个愚蠢而烦人的错误。花费了我太长时间才找出问题所在,但如果对某人有帮助的话就分享一下。


4
如果您使用v-if来有条件地渲染组件,那么您有两个解决问题的选项:
第一个是将元素包装在<no-ssr></no-ssr>标签中。
第二种方法是用v-show替换v-if这里是Vue文档的链接。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接