【问题标题】:Prevent component flickering on Vite / Vue 3 SSR component hydration防止组件在 Vite/Vue 3 SSR 组件补水上闪烁
【发布时间】:2021-08-20 02:20:43
【问题描述】:

我有一个组件在 SSR 中渲染得很好,但是当 Vue 3 进行水合时会闪烁(我认为)。

<template>
    <ul class="grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
      <li v-for="recipe in recipes" :key="recipe.id" class="col-span-1 flex flex-col text-center bg-white rounded-lg shadow divide-y divide-gray-200">
        <div class="flex-1 flex flex-col">
          <img class="w-full flex-shrink-0 mx-auto bg-black rounded-t-lg" :src="recipe.imageUrl" alt="" />
          <h3 class="text-gray-900 text-sm font-medium p-4">
            
              {{ recipe.title }}
          </h3>
        </div>
      </li>
    </ul>
    <Pagination v-model="page" :records="totalRecords" :per-page="12" :options="paginationOptions" @paginate="fetchRecipes" />
  </template>
  
  <script>
  
  import Pagination from 'v-pagination-3'
  import axios from 'axios'

  export default {
  
    components: {
      Pagination,
    },
    inject: ['paginationOptions'],
    data() {
      return {
        section: 'home',
        recipes: [],
        page: 3,
        totalRecords: 0,
      }
    },
    created() {
    },
    mounted() {
      this.fetchRecipes(this.page)
    },
    methods: {
      async fetchRecipes(page) {
        try {
          const url = 'http://local-api.local/api/fakeApi'
          const response = await axios.get(url, { params: { page } }).then((response) => {
            this.recipes = response.data.data.map(recipe => ({
              title: recipe.title,
              imageUrl: `http://test-server.local${recipe.thumbnailUrl}`,
            }))
            this.totalRecords = response.data.total
          })
        }
        catch (err) {
          if (err.response) {
            // client received an error response (5xx, 4xx)
            console.log('Server Error:', err)
          }
          else if (err.request) {
            // client never received a response, or request never left
            console.log('Network Error:', err)
          }
          else {
            console.log('Client Error:', err)
          }
        }
      },
    },
    serverPrefetch() {
      return this.fetchRecipes(this.page)
    },
  }
  </script>

我做错了什么?我一定尝试了 50 种设置 this.recipes 的方法(也在所有可能的生命周期挂钩中),但所有这些方法仍然会导致水合闪烁。

我正在使用 Vite(如果需要,可以使用 vite-ssr 插件)/Vue 3 SSR。请注意,闪烁后的组件似乎与 SSR 生成的版本相同,该版本在页面加载时显示(并且在源代码中)。

【问题讨论】:

  • 有一些假设(I think、seems)需要通过更深入的调试或托管项目来修复,以便人们查看实际发生的情况。
  • 是你的获取工作尝试控制台记录。如果是有效的回复,请告诉我
  • 抓取工作,如果我删除服务器预取部分,那么它只会在 Vue 启动后加载。相反是一样的,如果我删除挂载钩子中的获取,那么它会显示在从 Ssr 加载的页面上,但在 Vue 启动后消失。这就是让我认为 fetching 在 Ssr 和之后都有效的原因。但是 Vue 启动的时候还是有这个闪烁。项目太大,无法提供示例,但我会尽力想办法。
  • 控制台中的抓取结果也是一样的,这也是我惊讶它闪烁的另一个原因。

标签: vue.js server-side-rendering vuejs3 hydration vite


【解决方案1】:

我找到了解决办法。

我必须将所有数据放入 Vuex Store(在组件中),然后放入 main.ts,将其用作 initialState,并在加载客户端时,用 initialState 值填充存储。

在 main.ts 中创建存储:

import { createStore } from 'vuex'
const store = createStore({
  state() {
    return {}
  },
})

在我的组件中,填充存储:

this.$store.state.pageContent = {
  recipes: response.data.data,
  totalRecords: response.data.total,
  totalPages: response.data.last_page,
  page,
}

然后在 main.ts 中,但在 export default viteSSR():

if (import.meta.env.SSR) {
    initialState.initial = store.state
}
else {
    for (const item of Object.entries(initialState.initial)) {
        store.state[item[0]] = item[1]
    }

    //check if the store is identical as teh one generated by SSR
    console.log(store.state)
}

如果您需要查看更多结构,请注意我使用了这个 Vite SSR / Vue 3 样板项目:https://github.com/frandiox/vitesse-ssr-template

【讨论】:

    【解决方案2】:

    在示例中,您的 api 结果映射函数不包含 id。

                this.recipes = response.data.data.map(recipe => ({
                  title: recipe.title,
                  imageUrl: `http://test-server.local${recipe.thumbnailUrl}`,
                }))
    

    这意味着 :key=recipe.id 绑定到 undefined 并且 vue 无法跟踪每条记录的更改。更改映射函数以包含 id

                this.recipes = response.data.data.map(recipe => ({
                  id: recipe.id,
                  title: recipe.title,
                  imageUrl: `http://test-server.local${recipe.thumbnailUrl}`,
                }))
    

    如果我们愿意,也可以只包含整个对象

                this.recipes = response.data.data.map(recipe => ({
                  ...recipe,
                  imageUrl: `http://test-server.local${recipe.thumbnailUrl}`,
                }))
    

    当然,这是假设食谱上有一个 id 属性。

    【讨论】:

      猜你喜欢
      • 2019-06-09
      • 1970-01-01
      • 2020-04-06
      • 2018-11-03
      • 2010-11-05
      • 1970-01-01
      • 2020-09-19
      • 1970-01-01
      • 2021-06-24
      相关资源
      最近更新 更多