Svelte 5中派生嵌套字典的细粒度 reactivity 实现方案
Svelte 5 嵌套大对象的细粒度响应式派生方案
需求场景
你有一个规模可能很大的嵌套响应式状态:
let a = $state({ foo: {bar: {baz: {name: "x", value: 4}}}, unrelated: 42 });
需要派生一个对象b,满足b[x][y].newValue = a[x][y].value * 2,最终得到:
b = {foo: {bar: {baz: {newValue: 8}}}}
直接用$derived会在a的任意字段变更时全量重算整个b,无法适配大规模数据。需要在供组件加载的Class中实现细粒度响应,确保修改a[x][y].name这类非目标字段时,不会触发b对应节点的更新。
实现方案
核心思路是为每个包含value的节点创建独立的细粒度派生值,通过Proxy懒加载派生节点,避免提前遍历整个大对象,同时利用Svelte 5的$state和$derived实现精准依赖跟踪。
完整Class代码
class DerivedNestedState { #originalState; #derivedRoot; constructor(originalState) { this.#originalState = originalState; this.#derivedRoot = this.#createDerivedProxy(originalState); } get b() { return this.#derivedRoot; } #createDerivedProxy(originalNode) { // 非对象类型直接返回 if (typeof originalNode !== 'object' || originalNode === null) { return originalNode; } // 用$state创建响应式缓存,存储已生成的派生节点 const cache = $state({}); return new Proxy(cache, { get: (target, prop) => { // 缓存命中直接返回 if (prop in target) { return target[prop]; } // 生成newValue:仅当原节点存在value字段时创建独立派生值 if (prop === 'newValue' && 'value' in originalNode) { const derivedValue = $derived(originalNode.value * 2); target[prop] = derivedValue; return derivedValue; } // 递归处理嵌套对象属性 const originalChild = originalNode[prop]; if (typeof originalChild === 'object' && originalChild !== null) { const derivedChild = this.#createDerivedProxy(originalChild); target[prop] = derivedChild; return derivedChild; } // 非对象属性直接返回原数据 return originalNode[prop]; } }); } }
使用示例
// 初始化原状态 let a = $state({ foo: {bar: {baz: {name: "x", value: 4}}}, unrelated: 42 }); // 创建派生状态实例 const stateService = new DerivedNestedState(a); // 访问派生值 console.log(stateService.b.foo.bar.baz.newValue); // 输出 8 // 修改原状态的value,触发对应派生值更新 a.foo.bar.baz.value = 5; console.log(stateService.b.foo.bar.baz.newValue); // 输出 10 // 修改原状态的name,派生值不会更新(无多余计算) a.foo.bar.baz.name = "y"; console.log(stateService.b.foo.bar.baz.newValue); // 仍输出 10
关键特性说明
- 懒加载派生:通过Proxy拦截属性访问,只有当实际访问
b的某个节点时才会生成对应的派生内容,避免初始化时遍历整个大对象。 - 细粒度依赖:每个
newValue都是独立的$derived值,仅跟踪对应原节点的value字段,其他字段变更不会触发它的更新。 - 响应式缓存:用
$state维护缓存,确保派生节点的访问能被Svelte的响应式系统正确跟踪,组件使用时能自动更新。
内容的提问来源于stack exchange,提问作者tobiasBora
相关产品推荐
相关产品推荐

