gpt4 book ai didi

javascript - 为什么派生的 Svelte 商店在使用 `$` 和 `subscribe` 时会有不同的行为

转载 作者:行者123 更新时间:2023-12-05 02:27:57 27 4
gpt4 key购买 nike

我有一个无法在此项目中更改的数据模型。我正在尝试精简和简化下面的示例代码,所以希望这对我要重现的内容仍然有意义。

假设我有两家商店。一家商店拥有“容器”,另一家商店拥有“元素”——每个商店都在整个应用程序中独立使用,用于各种目的。默认情况下,容器仅包含项目 ID。有时,我想对某些页面上使用的数据进行反规范化。我使用派生存储并对每个容器对象进行非规范化,将它们变成 DenormalizedContainers。

假设我有一个用例,我想创建一个新的 Item 对象,然后将其添加到给定的 Container。据我所知,这将导致派生商店更新两次(一次用于更改项目,一次用于更改容器),因此使用该派生商店的任何东西都将更新两次(取决于它的调用方式) .

为什么从 subscribe$ 的行为会有所不同?

此外,虽然这对我来说不是什么大问题,但我很好奇是否有任何本地 Svelte-y 方法可以在不更改数据模型的情况下解决这个问题(同时仍然能够使用 subscribe API?

<script lang="ts">
import { derived, writable } from "svelte/store";

type Container = {
id: string;
itemIds: string[];
};

type Item = {
id: string;
name: string;
};

type DenormalizedContainer = {
id: string;
items: Item[];
};

const containers = writable<Container[]>([]);
const items = writable<Item[]>([]);

const denormalizedContainers = derived(
[containers, items],
([$containers, $items]): DenormalizedContainer[] => {
return $containers.map(({ id, itemIds }) => {
const denormalizedContainer = {
id,
items: itemIds.map((id) =>
$items.find((item) => {
item.id === id;
})
),
};
return denormalizedContainer;
});
}
);

function handleAddContainer() {
const value = Math.random() * 1000;
const newContainer: Container = { id: `new-container-${value}`, itemIds: [] };
containers.set([...$containers, newContainer]);
}
function handleAddItem() {
const value = Math.random() * 1000;
const newItem: Item = { id: `new-id-${value}`, name: `new-name-${value}` };
items.set([...$items, newItem]);
}
function handleAddBoth() {
const value = Math.random() * 1000;
const newItem: Item = { id: `new-id-${value}`, name: `new-name-${value}` };
const newContainer: Container = { id: `new-container-${value}`, itemIds: [newItem.id] };
items.set([...$items, newItem]);
containers.set([...$containers, newContainer]);
}

$: console.log(`$: There are ${$containers.length} containers`);
$: console.log(`$: There are ${$items.length} items`);
$: console.log(`$: There are ${$denormalizedContainers.length} denormalized containers`);

denormalizedContainers.subscribe((newValue) =>
console.log(
`Subscribe: There are ${$denormalizedContainers.length} denormalized containers`
)
);
</script>

<button class="block" on:click={() => handleAddContainer()}>Add container</button>
<button class="block" on:click={() => handleAddItem()}>Add item</button>
<button class="block" on:click={() => handleAddBoth()}>Add container and item</button>

结果:

单击每个按钮一次后,这里是日志:

页面加载:

Subscribe: There are 0 denormalized containers
$: There are 0 containers
$: There are 0 items
$: There are 0 denormalized containers

添加容器:

Subscribe: There are 1 denormalized containers
$: There are 1 containers
$: There are 1 denormalized containers

添加项目:

Subscribe: There are 1 denormalized containers
$: There are 1 items
$: There are 1 denormalized containers

同时添加:

Subscribe: There are 1 denormalized containers
Subscribe: There are 2 denormalized containers
$: There are 2 containers
$: There are 2 items
$: There are 2 denormalized containers

通过 $ 处理的更新是我一直想要的,但出于某种原因,手动 subscribe API 显示了两个更新。我在 .svelte 文件之外有几个位置,我需要在这些位置使用 subscribe API。

引用资料:

与此类似,但没有解释为什么 $ 的行为与 subscribe 不同。

Svelte Derived Store atomic / debounced updates

可能是潜在的问题?

https://github.com/sveltejs/svelte/issues/6730

可能在此处的 3.1/3.2 中进行了解释。微任务将 react 性语句捆绑在一起,并在 tick 结束时应用它们。而 subscribe 实时处理更新。注意:将我的 subscribe 代码包装在 tick 中只会使其使用相同的数据运行两次。

https://dev.to/isaachagoel/svelte-reactivity-gotchas-solutions-if-you-re-using-svelte-in-production-you-should-read-this-3oj3

最佳答案

如果您查看编译后的输出,您会发现响应式语句是在 update 上触发的,每个事件循环应该只发生一次。

    $$self.$$.update = () => {
if ($$self.$$.dirty & /*$containers*/ 256) {
$: console.log(`$: There are ${$containers.length} containers`);
}

if ($$self.$$.dirty & /*$items*/ 128) {
$: console.log(`$: There are ${$items.length} items`);
}

if ($$self.$$.dirty & /*$denormalizedContainers*/ 64) {
$: console.log(`$: There are ${$denormalizedContainers.length} denormalized containers`);
}
};

您可以创建自己的方法,在事件循环中捆绑更新。例如。使用 micro tasks :

function debouncedSubscribe(store, callback) {
let key;
return store.subscribe(value => {
key = {};
const currentKey = key;
queueMicrotask(() => {
if (key == currentKey)
callback(value);
});
});
}

debouncedSubscribe(denormalizedContainers, value =>
console.log(
`Subscribe: There are ${value.length} denormalized containers`
)
);

REPL example

没有办法再次取消微任务,所以旧的微任务通过一个键作废。确保只执行最后一个任务的其他方法可能更优雅。

通过setTimeout也可以做到,并且可以取消之前的,但是最小延迟会更大:

function debouncedSubscribe(store, callback) {
let timeout;
return store.subscribe(value => {
clearTimeout(timeout);
timeout = setTimeout(() => callback(value));
});
}

关于javascript - 为什么派生的 Svelte 商店在使用 `$` 和 `subscribe` 时会有不同的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72805846/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com