Svelte中如何从含store列表的store推导内层值?求更优方案
问题描述
我在Svelte中有一个包含派生store列表的store(我知道这可能不是最佳方案,但请姑且听之)。我想创建一个能获取这些store内层值的store,为此我编写了如下函数:
const deriveListOfStores = <T>(stores: Readable<Readable<T>[]>) => { const state = writable<T[]>([]); const unsubscribes = new Set<() => void>(); const subscribers = new Set<(v: T[]) => void>(); return { subscribe: (cb: (v: T[]) => void) => { subscribers.add(cb); if (subscribers.size === 1) { unsubscribes.add( stores.subscribe((storesValue) => { storesValue.forEach((store, storeIndex) => { unsubscribes.add( store.subscribe((v) => { state.update((s) => { s[storeIndex] = v; return s; }); }) ); }); }) ); } const unsub = state.subscribe(cb); return () => { subscribers.delete(cb); unsub(); if (subscribers.size === 0) { unsubscribes.forEach((u) => u()); unsubscribes.clear(); } } } }; };
我需要对该函数进行全面测试,但似乎应该有更简便的实现方式。或许这种需求本身不合理,但如果有更优方案(比如内置函数、第三方库,或是我的函数过于复杂),希望能了解相关方法。
更优实现方案
1. 用Svelte内置derive函数简化实现
Svelte自带的derive其实就能处理这种动态store列表的场景,完全不用手动管理订阅和取消订阅的逻辑,代码会简洁很多:
import { derive, get } from 'svelte/store'; const deriveListOfStores = <T>(stores: Readable<Readable<T>[]>) => { return derive(stores, ($stores, set) => { // 给每个子store绑定订阅,保存取消订阅的函数 const unsubs = $stores.map((store, index) => { return store.subscribe(value => { // 拿到当前所有子store的值,替换对应位置后更新 const currentValues = $stores.map(get); currentValues[index] = value; set(currentValues); }); }); // 父store更新时,先清理之前的子store订阅 return () => { unsubs.forEach(unsub => unsub()); }; }); };
这个实现靠derive自动处理订阅生命周期:当父store的列表变化时,会先调用返回的清理函数取消旧子store的订阅,再重新订阅新的列表,比手动维护subscribers和unsubscribes靠谱多了,也不容易出内存泄漏的问题。
2. 第三方库辅助(可选)
如果之后遇到更复杂的动态store场景,也可以试试svelte-derive-stores这类专门处理复杂派生的库,但就当前需求来说,内置的derive完全够用,没必要额外加依赖。
3. 原函数的优化建议
要是你想继续用自己写的函数,有两个地方可以改进:
- 父store更新时,没有取消之前子store的订阅,列表长度变化时会留着旧订阅,导致内存泄漏,得在父store的订阅回调里先清空旧的
unsubscribes里的子store订阅 - 更新数组时直接原地修改
s[storeIndex] = v,虽然Svelte会触发更新,但最好返回新数组保证不可变性,改成:return [...s.slice(0, storeIndex), v, ...s.slice(storeIndex + 1)];
这样更符合Svelte的最佳实践,也能避免一些奇怪的更新问题。
内容的提问来源于stack exchange,提问作者Zachiah
相关产品推荐
相关产品推荐

