Как сохранить состояние компонентов Svelte в виртуальном списке?

Я разрабатываю компонент диаграммы Ганта в Svelte, и он состоит из сотен компонентов строк, каждый из которых содержит несколько компонентов задачи. Задачи можно перетаскивать и перемещать. Производительность является приоритетом, и я использую виртуальный список для отображения только видимых строк.

<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>

Проблема в том, что когда я перемещаю задачи и прокручиваю строки из представления, они уничтожаются, а когда я возвращаюсь к ним, их состояние сбрасывается. Этот REPL с использованием svelte-virtual-list иллюстрирует проблему https://svelte.technology/repl?version=2.13.5&gist=bdb4c523d2e1cf7e3aef452e3f24d988 Мне интересно, как лучше всего справиться с этой ситуацией.

В настоящее время я использую объект задачи в качестве опоры и обновляю его, ссылка остается прежней, и прокрутка не влияет на нее.

<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} 
      {...task}/>

Это касается потенциально большего количества реквизитов, но я обновляю объект задачи в событии состояния следующим образом:

onstate({ changed, current, previous }) {
    const {task} = this.get();
    Object.assign(task, current);
    this.set({task});
},

Какой из них я должен предпочесть для повышения производительности или есть лучший способ справиться с этим?


3
1 248
1

Ответ:

Решено

Вам следует изменить массив данных вместо изменения объекта. Изменяя объект внутри массива, 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