Nuxt.js 错误:客户端渲染的虚拟 DOM 树与服务器渲染的内容不匹配

Posted

技术标签:

【中文标题】Nuxt.js 错误:客户端渲染的虚拟 DOM 树与服务器渲染的内容不匹配【英文标题】:Nuxt.js error: The client-side rendered virtual DOM tree is not matching server-rendered content 【发布时间】:2020-02-03 16:27:01 【问题描述】:

如何在 Nuxt 中修复此错误? 一个小费将不胜感激,我已经尝试了几个小时。


[Vue warn]: 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. (repeated 9 times)
value @ vendors.app.js:12923
value @ vendors.app.js:12923
value @ vendors.app.js:12923
(anonymous) @ vendors.app.js:12923
(anonymous) @ commons.app.js:20499
setTimeout (async)
(anonymous) @ commons.app.js:20482
./node_modules/vue/dist/vue.runtime.esm.js @ commons.app.js:20512
__webpack_require__ @ runtime.js:791
fn @ runtime.js:151
(anonymous) @ app.js:2069
./.nuxt/client.js @ app.js:3157
__webpack_require__ @ runtime.js:791
fn @ runtime.js:151
0 @ app.js:19627
__webpack_require__ @ runtime.js:791
checkDeferredModules @ runtime.js:46
webpackJsonpCallback @ runtime.js:33
(anonymous) @ app.js:1
vendors.app.js:12923 info You are running Vue in development mode.

当我从 AsyncData() 调用它时会发生这种情况:

      context.store.dispatch('retrievePosts', 'feedUrl': '/' + process.env.backendPublicApiPrefix + '/posts', 'selection': 'feed' );

在我的 Vuex 商店中:

export const actions = 

  async retrievePosts(context,  feedUrl, selection ) 
    context.commit("setLoading");
    context.commit("incrementPage");
    try 

      // only make an ajax call if we are not on or past the last page of posts
      if (!context.state.lastPage || (context.state.lastPage && (context.state.page <= context.state.lastPage))) 

        let finalUrl = feedUrl + '?page=' + context.state.page;

        finalUrl += '&viewBy=' + 'feed';
        const  data, status  = await this.$axios.get(finalUrl);
        if (!data) 
          throw ( statusCode: 404, message: 'Something went wrong while retrieving the post feed. The link you clicked on may be broken or no longer exist.' );
         else 
          if (data.last_page) 
            context.commit("setLastPage", data.last_page);
          
          if (data.data && (data.data.length > 0)) 
            context.commit("appendPosts", data.data);
           else 
            if (state.page < 1) 
              // context.state.error = "response.data.Error";
            
          
        
      

     catch (e) 
      throw ( statusCode: 404, message: 'Something went wrong while retrieving the post feed. The link you clicked on may be broken or no longer exist.' );
    
    context.commit("unsetLoading");
  ,
;


export const mutations = 
  setLoading(state) 
    state.loading = true;
  ,
  unsetLoading(state) 
    state.loading = false;
  ,
  setLastPage(state, pageId) 
    state.lastPage = pageId;
  ,
  resetPostFeed(state) 
    console.log('resetPostFeed');
    state.posts = [];
    state.page = 0;
    state.lastPage = null;
  ,
  setPostFeed(state,  postFeedSettings, posts ) 
    state.posts = posts;
  ,
  setPage(state, pageId) 
    state.page = pageId;
  ,
  incrementPage(state) 
    state.page++;
  ,
  appendPosts(state, newPosts) 
    state.posts = state.posts.concat(newPosts);
  ,
;

在我的模板中:

        <div v-for="post in postsFromStore">
          <news-feed-content-card
            :postObj="post"
            :authorObj="post.user"
          />
        </div>

请注意,如果我不从 asyncData() 调用它,那么一切都可以正常工作,但我需要为 s-s-r 这样做:

context.store.dispatch('retrievePosts', 'feedUrl': '/' + process.env.backendPublicApiPrefix + '/posts', 'selection': 'feed' );

我该如何解决这个问题?我觉得我在这里缺少一些东西,但我不能完全指出它。

编辑: no-s-s-r 标签已被弃用,并被替换为 使用 只是隐藏错误,并不能完全修复它。

我把它缩小到这个:

                <!-- this causes an error: The client-side rendered virtual DOM tree is not matching server-rendered content. -->
                <client-only>
                  <p class="tw-text-xs tw-pt-1 tw-text-gray-600" v-html="authorObj.bioHeadline"></p>
                </client-only>

authorObj.bioHeadline 的 html 类似:Founder at &lt;a href='https://www.example.com' target='_blank'&gt;Domain&lt;/a&gt;

我想将它包含在 s-s-r 模式中(删除仅限客户端的标签),但它会导致该错误:

[Vue warn]: 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. (repeated 9 times)

【问题讨论】:

Vuejs Error: The client-side rendered virtual DOM tree is not matching server-rendered的可能重复 @emirowski 我尝试了链接问题中建议的步骤,但这并不能解决问题。警告是相同的,但这是一个不同的问题。 AFAIK Nuxt 可能出于多种原因引发此错误。 编辑:将问题缩小到 v-html。仍然不确定为什么会这样。 我无法重现这个。请包括重现此内容所需的最小示例。您可能已经知道一个不相关的提示:加载商店数据时使用fetch 而不是asyncData。两者功能相同,除了asyncData 期望您返回一个将与组件data 属性合并的对象 @HMilbradt 刚刚发布了我是如何解决这个问题的 【参考方案1】:

想通了。

authorObj.bioHeadline 有一个链接,由于下面的代码,我有一个嵌套链接....我猜这是不允许的。

              <nuxt-link :to="getAuthorUrl">
                <p class="tw-font-bold kb-subtle-link tw-text-sm md:tw-text-lg"> authorObj.fullName </p>
                  <p class="tw-text-xs tw-pt-1 tw-text-gray-600" v-html="authorObj.bioHeadline"></p>
              </nuxt-link>

生成的 HTML:

<a href="/" class=""><p class="tw-font-bold kb-subtle-link tw-text-sm md:tw-text-lg">kp</p> <p class="tw-text-xs tw-pt-1 tw-text-gray-600">Founder at <a href="https://www.example.com" target="_blank">Domain</a></p></a>

【讨论】:

以上是关于Nuxt.js 错误:客户端渲染的虚拟 DOM 树与服务器渲染的内容不匹配的主要内容,如果未能解决你的问题,请参考以下文章

Vue中的浏览器关键渲染路径及虚拟DOM

vue渲染虚拟dom树原理

在 Nuxt.js 中完全呈现路由后运行函数

虚拟DOM和抽象语法树

nuxt之服务端渲染

Vue 警告客户端渲染的虚拟 DOM 树与服务器渲染的内容不匹配