如何根据 Svelte 中另一家商店的价值稳健地更新一家商店?

时间:2021-01-05 16:19:29

标签: javascript store svelte

我今天正在 Svelte 中编写我的第一个应用程序,在尝试创建一个非平凡的商店系统时遇到了一个基本的逻辑问题。

正如问题标题所说,我正在努力寻找一种编排商店更新的方法,其中一个商店以某种方式更新,具体取决于另一家商店的当前值。

文档说明可以像这样使用 get 获取商店的状态值:

import { get } from 'svelte/store';
const value = get(store);

但文档也说明:

<块引用>

通常,您应该通过订阅商店并使用随时间变化的值来读取商店的值。有时,您可能需要检索您未订阅的商店的值。 get 允许你这样做。 这通过创建订阅,读取值,然后取消订阅来实现。因此不推荐在热代码路径中使用。

这让我想知道我是否应该避免这种使用模式,即在没有真正订阅的情况下使用“get”获取商店的状态值,但我不确定我还能如何协调对多个商店的更新。

>

为了帮助解释,我写了一些我认为可以做到这一点的方法的例子,但所有这些例子似乎都不理想。

在我的示例中,我正在使用一个应用程序,您必须在其中射击目标,但目标的 HP 会下降,因为您的左轮手枪腔中的第一种弹药数量。

应用以输入侦听器开始。

#############################################################
# Step 1: Create an input listener
#############################################################

const keyboardListener = (() => {
    const subscribers = new Map();
    document.addEventListener('keyup', event => {
        if (event.code === 'Space') {
            [...subscribers].forEach(sub => sub({ eventType: 'fire' }));
        }
    });
    function subscribe(fn) {
        this.subscribers.set(fn, fn);
        return () => {
            this.subscribes.delete(fn);
        };
    }
    return {
        subscribe,
    };
})();

现在让我们尝试创建一个商店...

#############################################################
# Step 2a: Store architecture pattern 1: The problem
#############################################################

export const target = writable({
    name: 'cow',
    hp: 100,
});

export const ammo = writable([
    {
        type: 'armourPiercing',
        damage: 20,
    },
    {
        type: 'explosive',
        damage: 50,
    },
    {
        type: 'flash',
        damage: 1,
    }
]);

keyboardListener.subscribe(() => {

    // If we have ammo, get the first ammo and reduce the cows HP.
    target.set(currentTargetValue => ({
        ...currentTargetValue,
        hp: currentTargetValue.hp - ammoDamage // Where does ammoDamage come from?
    }));
});

上述商店的问题是,我不确定如何在目标商店的更新回调函数中获取弹药商店的值。

我意识到我可以通过将应用商店作为一个大而单一的东西来克服这个问题。这样我就可以在更新时获得所有的状态。

#############################################################
# Step 2b: Store architecture pattern 2: One big store
# (So we can access the current value of both stores during
# the update)
#############################################################

export const wholeAppStore = writable({
    target: {
        name: 'cow',
        hp: 100,
    },
    ammo: [
        {
            type: 'armourPiercing',
            damage: 20,
        },
        {
            type: 'explosive',
            damage: 50,
        },
        {
            type: 'flash',
            damage: 1,
        }
    ]
});

keyboardListener.subscribe(() => {

    // If we have ammo, get the first ammo and reduce the cows HP.
    wholeAppStore.set(currentWholeAppStoreValue => {
        const newAmmo = JSON.parse(JSON.stringify(currentWholeAppStoreValue.ammo));
        const firstAmmo = newAmmo.pop();
        const newTarget = JSON.parse(JSON.stringify(currentWholeAppStoreValue.target));
        newTarget.hp -= firstAmmo.damage;
        return {
            target: newTarget,
            ammo: newAmmo,
        };
    });
});

但是拥有一个大型的单一应用商店似乎并不明智。为其编写更新很尴尬,我想知道它在性能方面是否也不是很好。

那么让我们尝试使用 get 函数。

#############################################################
# Step 2c: Store architecture pattern 3: Use "get"?
#############################################################

export const target = writable({
    name: 'cow',
    hp: 100,
});

export const ammo = writable([
    {
        type: 'armourPiercing',
        damage: 20,
    },
    {
        type: 'explosive',
        damage: 50,
    },
    {
        type: 'flash',
        damage: 1,
    }
]);

keyboardListener.subscribe(() => {

    const ammoValue = get(ammo);
    const firstAmmo = [...ammoValue].pop();
    const ammoDamage = firstAmmo.damage;

    // If we have ammo, get the first ammo and reduce the cows HP.
    target.set(currentTargetValue => ({
        ...currentTargetValue,
        hp: currentTargetValue.hp - ammoDamage;
    }));
});

但由于文档中的消息传递,我不确定这种方法。另外,由于我只使用了 get 来提取值,因此弹药库尚未更新以从左轮手枪室中取出弹药。也许我们应该用两个单独的更新回调来完成。

#############################################################
# Step 2d: Store architecture pattern 4:
# Use messy global vars?
#############################################################

export const target = writable({
    name: 'cow',
    hp: 100,
});

export const ammo = writable([
    {
        type: 'armourPiercing',
        damage: 20,
    },
    {
        type: 'explosive',
        damage: 50,
    },
    {
        type: 'flash',
        damage: 1,
    }
]);

keyboardListener.subscribe(() => {

    let firstAmmo; // "Global" var
    ammo.set(currentAmmoValue => {
        const newAmmo = JSON.parse(JSON.stringify(currentAmmoValue));
        firstAmmo = newAmmo.pop();
        return newAmmo;
    });
    const ammoDamage = firstAmmo.damage;

    // If we have ammo, get the first ammo and reduce the cows HP.
    target.set(currentTargetValue => ({
        ...currentTargetValue,
        hp: currentTargetValue.hp - ammoDamage;
    }));
});

我认为这可行,但感觉很糟糕。

所以你看,我很难为更大的应用商店架构找到一个好的模式。当然,这实际上是一个相对简单的应用程序 - 现实世界的应用程序当然会复杂得多。

所以我想知道 - 是否有任何既定的模式来管理 Svelte 中的大型、相互关联的商店?

0 个答案:

没有答案