SvelteKit中如何从页面加载数据创建可写Svelte Store
解决方案:创建请求隔离的共享Store
针对你遇到的问题,核心痛点是服务端单例Store会跨请求共享数据,同时需要Store能被站点多组件复用,且支持客户端精细化修改。下面是具体实现方案:
1. 写一个Store工厂函数
不要直接创建单例Store,而是用工厂函数生成实例,确保服务端每个请求有独立Store,客户端保持单例。
新建$lib/stores/rates.js:
import { writable } from 'svelte/store'; export function createRatesStore(initialData) { const { subscribe, set, update } = writable(initialData); return { subscribe, set, update, // 可添加自定义业务方法,比如批量更新 updateRates: (newRates) => set({ ...$ratesStore, ...newRates }) }; }
2. 在根布局初始化并注入上下文
在根+layout.svelte中,用服务端加载的初始数据创建Store,再通过Svelte的上下文传递给所有子组件:
<script> import { setContext } from 'svelte'; import { createRatesStore } from '$lib/stores/rates'; // 接收+layout.server.js返回的初始数据 export let data; // 服务端每个请求会生成新Store实例,客户端仅初始化一次 const ratesStore = createRatesStore(data.rates); // 将Store注入上下文,全局可访问 setContext('ratesStore', ratesStore); </script> <slot />
根+layout.server.js保持原有数据加载逻辑即可:
export async function load({ depends }) { depends('data:rates'); // 仅当需要主动失效数据时保留 const rates = await fetch('你的数据源').then(res => res.json()); return { rates }; }
3. 在任意组件中使用Store
所有需要用到数据的组件,通过getContext获取Store实例,服务端会自动绑定当前请求的实例,客户端则复用单例:
<script> import { getContext } from 'svelte'; const ratesStore = getContext('ratesStore'); </script> <!-- 订阅数据 --> <div>美元汇率:{$ratesStore.usd}</div> <!-- 客户端自定义时机修改数据 --> <button on:click={() => ratesStore.set({ usd: 7.3 })}>手动更新汇率</button>
4. 客户端精细化控制示例
如果需要从API重新拉取数据并更新,完全不需要依赖load函数或depends,直接在组件里实现:
<script> const ratesStore = getContext('ratesStore'); async function refreshRates() { const res = await fetch('/api/latest-rates'); const latestRates = await res.json(); ratesStore.set(latestRates); } </script> <button on:click={refreshRates} disabled={$ratesStore.isLoading}> {$ratesStore.isLoading ? '刷新中...' : '刷新最新汇率'} </button>
为什么之前的方案有问题?
- 直接导入单例Store并调用
.set():服务端的Store是全局共享的,所有用户请求会共用同一实例,导致数据交叉污染。 - 使用
depends('data:rates'):这是用来标记数据依赖,触发invalidate时重新运行服务端load函数,不是客户端直接修改数据的方案,自然无法精细化控制。
内容的提问来源于stack exchange,提问作者LAZ
相关产品推荐
相关产品推荐

