Svelte派生存储如何仅在目标子对象属性变更时触发回调?
问题
我创建了一个用于表单提交的可写存储(writable),结构如下:
const store = writable({ network: { content: "", error: "" }, name: { content: "", error: "" } });
我希望某些检查仅在父属性的子项发生变更时触发,因此为每个子对象创建了派生存储(derived store):
const networkStore = derived(store, ($store) => $store.network); networkStore.subscribe((network) => { console.log('network handler fired'); }); const nameStore = derived(store, ($store) => $store.name); nameStore.subscribe((name) => { console.log('name handler fired'); });
但无论修改哪部分数据,这两个回调都会触发。目前我发现只有针对具体值属性(例如字符串类型的子属性)创建派生存储时,才能实现事件隔离:
const nameStore = derived(store, ($store) => $store.name.content); nameStore.subscribe((nameContent) => { console.log('nameContent handler fired'); });
上述示例仅在$store.name.content属性被修改时才会触发回调。
我的问题是:是否可以订阅大对象的子对象,且仅当该子对象内的属性发生修改时触发对应的回调?
解决方案
当然可以实现,核心原因是Svelte的派生存储默认使用引用相等性检查——只要父对象的引用发生变化(哪怕只是修改了其他子对象),派生存储就会认为值已更新,触发回调。要实现子对象级别的订阅隔离,有两种实用方案:
1. 自定义派生存储的相等性检查
在创建派生存储时,传入第三个参数自定义相等性判断逻辑,手动对比子对象的内容是否真正变化:
const networkStore = derived( store, ($store) => $store.network, (a, b) => { // 对比子对象的所有属性 return a.content === b.content && a.error === b.error; } ); networkStore.subscribe((network) => { console.log('network handler fired'); }); const nameStore = derived( store, ($store) => $store.name, (a, b) => { return a.content === b.content && a.error === b.error; } ); nameStore.subscribe((name) => { console.log('name handler fired'); });
这样只有当network或name子对象内部的属性真正变化时,对应的订阅回调才会触发。
2. 拆分独立的可写存储
如果业务允许,直接将子对象拆分为单独的可写存储,从根源上隔离状态变更:
// 拆分独立存储 const networkStore = writable({ content: "", error: "" }); const nameStore = writable({ content: "", error: "" }); // 可选:如果需要统一的根存储,用派生存储组合 const rootStore = derived( [networkStore, nameStore], ([network, name]) => ({ network, name }) ); // 订阅子存储,只会在自身变更时触发 networkStore.subscribe(() => { console.log('network handler fired'); }); nameStore.subscribe(() => { console.log('name handler fired'); });
这种方案更简洁,状态管理更清晰,适合表单这类子模块独立性较强的场景。
内容的提问来源于stack exchange,提问作者Nick Jennings
相关产品推荐
相关产品推荐

