#await 块在#each 循环和 DOM 刷新性能问题
#await blocks in an #each loop and DOM refresh performance issue
我循环浏览从 Sveltekit 商店检索到的项目,并异步检索每个迭代项目的附加信息。
简化代码如下:
{#each $order.itemIDs as itemID}
{#await calcItemPrice(itemID)}
{:then price}
{#await itemFor(itemID)}
{:then item}
<OrderItem itemID={item._id} description={item.description} price={price} />
{/await}
{/await}
{/each}
另外,UI 允许向 $order.itemIDs
添加更多条目(使用 update
),这会触发每个循环中 DOM 元素的重绘。关键问题是:
- 每次添加项目时重新绘制
#each
循环意味着每次都为每个项目重新进行异步调用。随着越来越多的项目被添加,这显然会影响性能。
- 在此(缓慢的)重绘过程中,
#each
中的 DOM 元素会闪烁,因为它们会暂时消失并在执行所有提取查询后重新出现。
我认为我的问题有两个方面:
- 我怎样才能使添加新的
itemID
到 $order.itemIDs
不会触发所有渲染元素的重绘,而只是将新元素附加到 DOM?
- 我应该在什么时候 运行 我的异步调用来提高渲染性能? (我感觉 运行ning 在渲染时不是最理想的)
首先要做的是使用键控的每个块,这将使只有已更改的项目实际获得 re-rendered:
{#each $order.itemIDs as itemID (itemID)}
确保您使用的ID是唯一的,以上只是一个例子!
也就是说,我不会在循环中使用 await 块,我认为那只是在问问题。看看是否可以在其他地方加载这些项目,也许第一个 运行.
有一个等待块
如何实现这当然在很大程度上取决于您的用例和要求,一个想法可能是从商店中填充另一个数组:
let items = []
async function loadItems(stored) {
const changed = stored.filter(s => !items.some(s => s.id == s));
const result = await Promise.all(changed.map(async => calcsHere))
items = [...items, ...result];
}
$: loadItems($order.itemIDs)
上面的代码声明了一个数组 items
和一个函数 loadItems
,每次商店更改时都会 运行。
函数本身做的是:
- 过滤掉新的项目(不重新获取它们)
- 同时触发所有新项目的提取并等待所有项目完成 return
- 将新项目添加到旧项目中
(请注意,我在该代码中可能有很多错误,这只是一个笼统的概念)
这样做的一个缺点是你不能有一个 'loading' 指示器,尽管你可以通过使用某种 'loading' 标志将新项目推入数组然后覆盖来做到这一点他们。
我循环浏览从 Sveltekit 商店检索到的项目,并异步检索每个迭代项目的附加信息。
简化代码如下:
{#each $order.itemIDs as itemID}
{#await calcItemPrice(itemID)}
{:then price}
{#await itemFor(itemID)}
{:then item}
<OrderItem itemID={item._id} description={item.description} price={price} />
{/await}
{/await}
{/each}
另外,UI 允许向 $order.itemIDs
添加更多条目(使用 update
),这会触发每个循环中 DOM 元素的重绘。关键问题是:
- 每次添加项目时重新绘制
#each
循环意味着每次都为每个项目重新进行异步调用。随着越来越多的项目被添加,这显然会影响性能。 - 在此(缓慢的)重绘过程中,
#each
中的 DOM 元素会闪烁,因为它们会暂时消失并在执行所有提取查询后重新出现。
我认为我的问题有两个方面:
- 我怎样才能使添加新的
itemID
到$order.itemIDs
不会触发所有渲染元素的重绘,而只是将新元素附加到 DOM? - 我应该在什么时候 运行 我的异步调用来提高渲染性能? (我感觉 运行ning 在渲染时不是最理想的)
首先要做的是使用键控的每个块,这将使只有已更改的项目实际获得 re-rendered:
{#each $order.itemIDs as itemID (itemID)}
确保您使用的ID是唯一的,以上只是一个例子!
也就是说,我不会在循环中使用 await 块,我认为那只是在问问题。看看是否可以在其他地方加载这些项目,也许第一个 运行.
有一个等待块如何实现这当然在很大程度上取决于您的用例和要求,一个想法可能是从商店中填充另一个数组:
let items = []
async function loadItems(stored) {
const changed = stored.filter(s => !items.some(s => s.id == s));
const result = await Promise.all(changed.map(async => calcsHere))
items = [...items, ...result];
}
$: loadItems($order.itemIDs)
上面的代码声明了一个数组 items
和一个函数 loadItems
,每次商店更改时都会 运行。
函数本身做的是:
- 过滤掉新的项目(不重新获取它们)
- 同时触发所有新项目的提取并等待所有项目完成 return
- 将新项目添加到旧项目中
(请注意,我在该代码中可能有很多错误,这只是一个笼统的概念)
这样做的一个缺点是你不能有一个 'loading' 指示器,尽管你可以通过使用某种 'loading' 标志将新项目推入数组然后覆盖来做到这一点他们。