如何在虚拟列表中保留 Svelte 组件的状态?
How to persist state of Svelte components in a virtual list?
我正在 Svelte 中开发甘特图组件,它由 100 行组件组成,每个组件包含多个任务组件。可以拖动和移动任务。性能是重中之重,我使用虚拟列表只呈现可见行。
<div class="scrollable">
{#each visibleRows as row (row.id)}
<div class="row">
{#each row.tasks as task (task.id)}
<Task from={task.from} to={to}/>
{/each}
</div>
{/each}
</div>
问题是当我四处移动任务并将行滚动到视图之外时,它们会被销毁,而当我滚动回它们时,它们的状态会被重置。这个使用 svelte-virtual-list 的 REPL 说明了这个问题 https://svelte.technology/repl?version=2.13.5&gist=bdb4c523d2e1cf7e3aef452e3f24d988
我想知道处理这种情况的最佳方法是什么。
目前我正在使用任务对象作为道具并更新它,引用保持不变并且滚动不会影响它。
<Task {task}/>
但是在 Task 中我像这样更新状态
const {task} = this.get();
task.from = new Date();
this.set({task});
并且模板使用 task.propname 引用每个道具,我不确定这样做是否是一种巧妙的方式,因为不清楚到底设置了什么。
我可以像这样绑定任务变量
<Task bind:from=task.from
bind:to={task.to}/>
但是需要绑定很多变量(超过10个),我想允许未来可能的插件从任务组件内部更新新道具。
<Task {task}
{...task}/>
这处理了潜在的更大数量的道具,但我像这样更新状态事件中的任务对象:
onstate({ changed, current, previous }) {
const {task} = this.get();
Object.assign(task, current);
this.set({task});
},
为了性能我应该更喜欢其中的哪一个,或者有更好的方法来处理这个问题吗?
您应该改变数据数组而不是改变对象。通过改变数组中的对象,Svelte 不知道你的数组已经改变。
使用 Svelte v3 并使用存储而不是普通数组,改变数组会像这样:
// data.js
...
import { writable } from 'svelte/store';
export default writable(getData())
...
// App.svelte
<script>
...
import items from './data.js';
function updateContent(item) {
const index = $items.indexOf(item)
$items = [
...$items.slice(0, index),
{ ...item, content: 'updated content' },
...$items.slice(index + 1)
]
}
...
</script>
<VirtualList items={$items} let:item bind:start bind:end>
...
<button on:click={() => updateContent(item)}>update content</button>
...
</VirtualList>
完整示例:https://svelte.dev/repl?version=3.0.0-beta.28&gist=f8c7e96bb34906db2c3a5e85b4840017
您可以在此处阅读有关更新数组和对象的更多信息:https://svelte.dev/tutorial/updating-arrays-and-objects
我正在 Svelte 中开发甘特图组件,它由 100 行组件组成,每个组件包含多个任务组件。可以拖动和移动任务。性能是重中之重,我使用虚拟列表只呈现可见行。
<div class="scrollable">
{#each visibleRows as row (row.id)}
<div class="row">
{#each row.tasks as task (task.id)}
<Task from={task.from} to={to}/>
{/each}
</div>
{/each}
</div>
问题是当我四处移动任务并将行滚动到视图之外时,它们会被销毁,而当我滚动回它们时,它们的状态会被重置。这个使用 svelte-virtual-list 的 REPL 说明了这个问题 https://svelte.technology/repl?version=2.13.5&gist=bdb4c523d2e1cf7e3aef452e3f24d988 我想知道处理这种情况的最佳方法是什么。
目前我正在使用任务对象作为道具并更新它,引用保持不变并且滚动不会影响它。
<Task {task}/>
但是在 Task 中我像这样更新状态
const {task} = this.get();
task.from = new Date();
this.set({task});
并且模板使用 task.propname 引用每个道具,我不确定这样做是否是一种巧妙的方式,因为不清楚到底设置了什么。
我可以像这样绑定任务变量
<Task bind:from=task.from
bind:to={task.to}/>
但是需要绑定很多变量(超过10个),我想允许未来可能的插件从任务组件内部更新新道具。
<Task {task}
{...task}/>
这处理了潜在的更大数量的道具,但我像这样更新状态事件中的任务对象:
onstate({ changed, current, previous }) {
const {task} = this.get();
Object.assign(task, current);
this.set({task});
},
为了性能我应该更喜欢其中的哪一个,或者有更好的方法来处理这个问题吗?
您应该改变数据数组而不是改变对象。通过改变数组中的对象,Svelte 不知道你的数组已经改变。
使用 Svelte v3 并使用存储而不是普通数组,改变数组会像这样:
// data.js
...
import { writable } from 'svelte/store';
export default writable(getData())
...
// App.svelte
<script>
...
import items from './data.js';
function updateContent(item) {
const index = $items.indexOf(item)
$items = [
...$items.slice(0, index),
{ ...item, content: 'updated content' },
...$items.slice(index + 1)
]
}
...
</script>
<VirtualList items={$items} let:item bind:start bind:end>
...
<button on:click={() => updateContent(item)}>update content</button>
...
</VirtualList>
完整示例:https://svelte.dev/repl?version=3.0.0-beta.28&gist=f8c7e96bb34906db2c3a5e85b4840017
您可以在此处阅读有关更新数组和对象的更多信息:https://svelte.dev/tutorial/updating-arrays-and-objects