SvelteKit中非页面组件的数据加载方案咨询
解决SvelteKit非页面组件的类型安全数据加载问题
1. 用server$/client$实现组件级按需加载
SvelteKit的server$和client$函数可以让你在组件内直接定义服务端/客户端专属的数据逻辑,自带类型推导,完全不需要依赖路由目录的+page.ts。
比如在组件里按需获取用户数据:
<script lang="ts"> import { server$ } from '$lib/server'; // 服务端专属的加载函数,自动类型推导返回值 const fetchUser = server$(async (userId: string) => { const res = await fetch(`https://api.example.com/users/${userId}`); if (!res.ok) throw new Error('Failed to fetch user'); return res.json() as { id: string; name: string; email: string }; }); let user: Awaited<ReturnType<typeof fetchUser>> | null = null; let error: string | null = null; // 组件挂载后按需加载,或者触发事件时加载 async function loadUser() { try { user = await fetchUser('123'); } catch (err) { error = (err as Error).message; } } </script> <button on:click={loadUser}>加载用户数据</button> {#if error} <p class="error">{error}</p> {:else if user} <div> <h3>{user.name}</h3> <p>{user.email}</p> </div> {/if}
server$里的代码只会在服务端执行,避免敏感逻辑暴露到客户端- 返回值自动生成类型,组件内使用时类型完全安全
2. 封装可复用的类型安全加载器
如果多个组件需要类似的加载逻辑,可以封装一个通用的加载组件,用泛型保证类型一致性:
<!-- src/lib/components/ComponentLoader.svelte --> <script lang="ts"> export let loadFn: () => Promise<T>; export let fallback: string = '加载中...'; let data: T | null = null; let error: string | null = null; let loading = true; async function loadData() { loading = true; try { data = await loadFn(); } catch (err) { error = (err as Error).message; } finally { loading = false; } } $: if (loadFn) loadData(); </script> {#if loading} <p>{fallback}</p> {:else if error} <p class="error">{error}</p> {:else if data} <slot {data} /> {/if}
使用时直接传入组件专属的加载函数,自动继承类型:
<script lang="ts"> import ComponentLoader from '$lib/components/ComponentLoader.svelte'; import { server$ } from '$lib/server'; const fetchProducts = server$(async () => { const res = await fetch('https://api.example.com/products'); return res.json() as { id: string; name: string; price: number }[]; }); </script> <ComponentLoader {loadFn}> {#snippet slot({ data })} <ul> {#each data as product} <li>{product.name} - ${product.price}</li> {/each} </ul> {/snippet} </ComponentLoader>
3. 集中管理加载逻辑,脱离路由耦合
把所有组件的加载函数集中放在src/lib/loaders目录下,按功能分类,完全和路由目录解耦:
src/ ├─ lib/ │ ├─ loaders/ │ │ ├─ user.ts │ │ ├─ products.ts │ │ └─ orders.ts │ └─ components/ └─ routes/
比如src/lib/loaders/products.ts:
import { server$ } from '$lib/server'; export const fetchProducts = server$(async (limit: number = 10) => { const res = await fetch(`https://api.example.com/products?limit=${limit}`); if (!res.ok) throw new Error('Failed to fetch products'); return res.json() as { id: string; name: string; price: number }[]; });
组件里直接导入使用即可,完全不受路由目录结构限制。
4. 按需加载控制
如果不需要页面初始化时加载数据,可以把加载逻辑绑定到用户交互(比如点击、滚动)或者组件的生命周期钩子(比如onMount仅在客户端触发):
<script lang="ts"> import { onMount } from 'svelte'; import { fetchProducts } from '$lib/loaders/products'; let products: Awaited<ReturnType<typeof fetchProducts>> | null = null; // 组件挂载后(客户端)才加载 onMount(async () => { products = await fetchProducts(5); }); </script> {#if products} <!-- 渲染产品列表 --> {/if}
内容的提问来源于stack exchange,提问作者geeko
相关产品推荐
相关产品推荐

