Nuxt电商分页Hydration节点不匹配及生产环境空白问题求助
问题:Nuxt电商站点店铺页 hydration 不匹配及生产模式空白问题
问题现象
- 开发模式下访问店铺页(含带
page参数的URL)会出现hydration节点不匹配警告 - 生产模式下直接访问
http://localhost:3000/shop?page=1时,page=1内容闪显后页面变为空白 - 点击菜单进入店铺页、点击分页按钮、浏览器前进后退操作均正常
开发模式警告信息
[Vue warn]: Hydration node mismatch: - Client vnode: Symbol(v-cmt) - Server rendered DOM: <div class="grid grid-cols-1 gap-3 phone:grid-cols-4 laptop:grid-cols-8 justcol" data-v-7db6cc15><!--[--><div data-v-7db6cc15>…</div><div class="card text-center flex flex-col justify-between" data-v-7db6cc15 data-v-fe52aa40>…</div></div><div data-v-7db6cc15>…</div><div data-v-7db6cc15>…</div><div data-v-7db6cc15>…</div><!--]--></div>
相关代码
店铺页模板代码
<template> <div v-if="paginproducts" class="grid grid-cols-1 gap-3 phone:grid-cols-4 laptop:grid-cols justcol" > <div v-for="product in paginproducts" :key="product._id"> <ProductCard v-if="paginproducts" :product="product" /> </div> </div> <Pagination v-if="paginationlenght" :paginationlenght="paginationlenght" :paginationrow="paginationrow" @clicked="changePagination" /> </template>
店铺页脚本代码
<script setup> const route = useRoute(); const router = useRouter(); const runtimeConfig = useRuntimeConfig(); const apitKey = runtimeConfig.public.apiToken; const paginproducts = ref(null); const paginationlenght = ref(null); const paginationrow = 4; const errorhandler = ref(false); //Fetch Products from API const fetchProducts = async (skip) => { const { data: products, error } = await useFetch( `${runtimeConfig.public.apiUrl}/api/content/items/Products? limit=${paginationrow}&skip=${skip}`, { method: "GET", headers: { "api-key": apitKey, }, } ); for (let i in products._value) { if (products._value.data.length) { paginproducts.value = {}; paginationlenght.value = {}; paginproducts.value = products._value.data; paginationlenght.value = products._value.meta; errorhandler.value = false; } else { errorhandler.value = true; } } if (error.value || errorhandler.value) { unwatch(); paginproducts.value = null; paginationlenght.value = null; setTimeout(() => { showError({ statusCode: 404, statusMessage: "Page not found.", data: "Generated from Category page.", }); }, 1000); } }; const changePagination = (changerow) => { let queypage = changerow + 1; router.push({ path: "/shop", query: { page: queypage } }); console.log("pagination"); if (process.browser) { window.scrollTo({ top: 0, behavior: "smooth" }); } }; onMounted(() => { if (!route.query.page || route.query.page === "") { router.replace({ path: "/shop", query: { page: "1" } }); } }); const unwatch = watch( () => route.query, () => { if (route.query.page !== "") { let skipping = parseInt(route.query.page) - 1; skipping = skipping * paginationrow; fetchProducts(skipping); } }, { immediate: true } ); </script>
分页组件模板代码
<template> <div v-if="totalButtons != 1" class="mt-6 w-full"> <ul class="flex justify-center w-auto mx-auto place-items-center flex-col sm:flex-row" > <li class="mx-2 my-6 sm:my-auto"> <button @click="onClickNumber(0)" :disabled="activeButton === 0" type="button" class="pagbutton" :class="{ active: activeButton === 0 }" > <span class="material-icons">keyboard_double_arrow_left </span> </button> </li> <li class="mx-2 my-6 sm:my-auto"> <button @click="onClickNumber(activeButton - 1)" :disabled="activeButton === 0" type="button" class="pagbutton" :class="{ active: activeButton === 0 }" > <span class="material-icons"> chevron_left </span> </button> </li> <li v-for="(number, index) in totalButtons" :key="index" class="mx-2 my-6 sm:my-auto" > <p> <button @click="onClickNumber(index)" :disabled="activeButton === index" type="button" class="pagbutton buttonnumber" :class="{ activenumber: activeButton === index }" > {{ number }} </button> </p> </li> <li class="mx-2 my-6 sm:my-auto"> <button @click="onClickNumber(activeButton + 1)" :disabled="activeButton === totalButtons - 1" type="button" class="pagbutton" :class="{ active: activeButton === totalButtons - 1 }" > <span class="material-icons"> chevron_right </span> </button> </li> <li class="mx-2 my-6 sm:my-auto"> <button @click="onClickNumber(totalButtons - 1)" :disabled="activeButton === totalButtons - 1" type="button" class="pagbutton" :class="{ active: activeButton === totalButtons - 1 }" > <span class="material-icons">keyboard_double_arrow_right </span> </button> </li> </ul> </div> </template>
分页组件脚本代码
<script setup> const route = useRoute(); const { paginationlenght, paginationrow } = defineProps([ "paginationlenght", "paginationrow", ]); const emit = defineEmits(["clicked"]); const activeButton = ref(0); let totalButtons; if (paginationlenght && paginationlenght.total > 0) { const calcButtons = paginationlenght.total % paginationrow; if (calcButtons === 0) { totalButtons = paginationlenght.total / paginationrow; } else { totalButtons = (paginationlenght.total - calcButtons) / paginationrow + 1; } } else { totalButtons = 1; } const onClickNumber = (changenumber) => { activeButton.value = changenumber; emit("clicked", activeButton.value); }; const unwatch = watch( async () => route.query, async () => { if (route.query.page) { activeButton.value = parseInt(route.query.page) - 1; } }, { immediate: true } ); </script>
排查尝试与求助
怀疑问题源于分页组件中的Google Icons,已尝试为其添加client-only标签,但问题仍存在,求解决方案。
内容的提问来源于stack exchange,提问作者Klas
相关产品推荐
相关产品推荐

