SvelteKit布局中面包屑渲染异常问题求助
面包屑组件在Svelte布局中渲染时的旧数据残留问题解决
需求
- 在
__layout.svelte中渲染Breadcrumb组件,通过可写存储$breadcrumb读取展示数据,$breadcrumb的数据由各route.svelte文件的loader提供的数据填充。
问题
启动服务(yarn dev)或通过adapter-static构建渲染页面时,面包屑会先显示其他路由的旧数据,页面水合(hydration)后才更新到当前路由的正确数据。本质是Breadcrumb组件在当前视图数据加载完成前就已渲染,使用了上一页遗留的存储值。虽然在每个单独路由中渲染Breadcrumb组件能解决该问题,但希望保留在__layout文件中,寻求可行方案。
代码示例
__layout.svelte
<script> import Breadcrumb from '../components/Breadcrumb.svelte' </script> <div class="wrap"> <MainMenu /> <div class="container"> <Breadcrumb /> <slot /> </div> ...
store.js
import { writable } from 'svelte/store'; export const breadcrumb = writable({});
components/Breadcrumb.svelte
<script> import { breadcrumb } from '$lib/store'; </script> {#if Object.keys($breadcrumb).length} <ul class="breadcrumb noprn"> <li><a href="/">Accueil</a></li> {#each Object.entries($breadcrumb) as [label, url] (label)} <li> {#if url} <a href={url}>{label}</a> {:else} <h1>{label}</h1> {/if} </li> {/each} </ul> {/if}
routes/product/[id].svelte
<script> export let reference; // << 数据从这里传入 let product = reference.product; import { breadcrumb } from '$lib/store'; $breadcrumb = { Catalogue: '/catalogue', [reference.category.name]: `/catalogue/${reference.category.slug}`, [reference.brand.name]: `/catalogue/brands/${reference.brand.id}`, [product.name]: null } </script> ...
解决方案
方案1:利用SvelteKit页面数据传递(推荐)
放弃全局store,改用SvelteKit原生的布局与路由数据传递机制,从根源避免旧数据问题:
- 在路由的
load函数中返回面包屑数据:
// routes/product/[id].js export async function load({ params }) { // 替换为实际获取reference数据的逻辑 const reference = await fetchProductDetail(params.id); return { breadcrumb: { Catalogue: '/catalogue', [reference.category.name]: `/catalogue/${reference.category.slug}`, [reference.brand.name]: `/catalogue/brands/${reference.brand.id}`, [reference.product.name]: null } }; }
- 在
__layout.svelte中接收路由数据并传递给Breadcrumb:
<script> import Breadcrumb from '../components/Breadcrumb.svelte'; export let data; // 接收路由load返回的数据 </script> <div class="wrap"> <MainMenu /> <div class="container"> <Breadcrumb items={data.breadcrumb} /> <slot /> </div> ...
- 修改Breadcrumb组件,改用props接收数据:
<script> export let items = {}; </script> {#if Object.keys(items).length} <ul class="breadcrumb noprn"> <li><a href="/">Accueil</a></li> {#each Object.entries(items) as [label, url] (label)} <li> {#if url} <a href={url}>{label}</a> {:else} <h1>{label}</h1> {/if} </li> {/each} </ul> {/if}
这种方式完全贴合SvelteKit数据流设计,服务端渲染阶段就能拿到正确的面包屑数据,不会出现旧数据残留。
方案2:路由切换时重置Store
如果坚持使用全局store,可通过路由导航事件清空旧数据:
- 在
__layout.svelte中监听路由切换,重置面包屑store:
<script> import Breadcrumb from '../components/Breadcrumb.svelte'; import { breadcrumb } from '$lib/store'; import { page } from '$app/stores'; import { onMount } from 'svelte'; onMount(() => { const unsubscribe = page.subscribe(() => { // 路由切换时立即清空面包屑 breadcrumb.set({}); }); return unsubscribe; }); </script>
- 确保路由组件在数据加载完成后再更新store:
<script> export let reference; import { breadcrumb } from '$lib/store'; import { onMount } from 'svelte'; onMount(() => { // 数据加载完成后再设置面包屑 $breadcrumb = { Catalogue: '/catalogue', [reference.category.name]: `/catalogue/${reference.category.slug}`, [reference.brand.name]: `/catalogue/brands/${reference.brand.id}`, [reference.product.name]: null }; }); </script>
路由切换时先清空面包屑,直到当前路由数据加载完成后再渲染新内容,避免旧数据展示。
方案3:给面包屑数据绑定路由标识
让面包屑数据与当前路由关联,只渲染匹配的内容:
- 修改store结构,加入当前路由path:
// store.js import { writable } from 'svelte/store'; export const breadcrumb = writable({ path: '', items: {} });
- 路由组件设置面包屑时传入当前路由path:
<script> export let reference; import { breadcrumb } from '$lib/store'; import { page } from '$app/stores'; $breadcrumb = { path: $page.url.pathname, items: { Catalogue: '/catalogue', [reference.category.name]: `/catalogue/${reference.category.slug}`, [reference.brand.name]: `/catalogue/brands/${reference.brand.id}`, [reference.product.name]: null } }; </script>
- 修改Breadcrumb组件,只渲染与当前路由匹配的面包屑:
<script> import { breadcrumb } from '$lib/store'; import { page } from '$app/stores'; </script> {#if $breadcrumb.path === $page.url.pathname && Object.keys($breadcrumb.items).length} <ul class="breadcrumb noprn"> <li><a href="/">Accueil</a></li> {#each Object.entries($breadcrumb.items) as [label, url] (label)} <li> {#if url} <a href={url}>{label}</a> {:else} <h1>{label}</h1> {/if} </li> {/each} </ul> {/if}
即使store中有旧数据,也只会显示和当前路由匹配的内容。
内容的提问来源于stack exchange,提问作者Sélim Achour
相关产品推荐
相关产品推荐

