You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 13:17:36