当 Svelte 重用父 dom 元素时如何确保仅本地转换

Mar*_*eth 4 javascript svelte svelte-transition

在 Svelte 中,我有一个组件用于在两个不同的列表中显示项目。当这些项目从一个列表移动到另一个列表时,它们会使用过渡来动画进出。

但是,我也有一种方法可以过滤屏幕上显示的内容。显示一组新项目将使用相同的组件,但具有不同的数据。在这种情况下,我不希望发生过渡动画。我认为添加local修饰符可以解决问题,但 Svelte 似乎没有将父元素放到列表中,而是重用它并在现有列表 DOM 元素中添加新数据。

我试图重现我在下面的示例代码中看到的内容。

通缉行为:

  1. 单击待办事项会将待办事项从一个列表切换到另一个列表。
  2. 单击“切换类别”将切换列出的 TODO,而不会对<li>添加或删除的 TODO进行动画处理。

实际行为:

  1. 按预期发生。
  2. 切换到的待办事项与动画一起执行。

我怎样才能改变我的例子,以获得我想要的效果?

App.svelte:

<script>
    import Todos from './Todos.svelte';

    let todos = [
        { id: 1, category: 'personal', name: 'Walk dog', done: false },
        { id: 2, category: 'personal', name: 'Take out trash', done: false },
        { id: 3, category: 'work', name: 'Make login page functional', done: false },
        { id: 4, category: 'work', name: 'Make login page elegant', done: false }
    ];

    let currentCategory = 'personal';
    const toggleCategory = () => {
        currentCategory = currentCategory == 'personal' ? 'work' : 'personal';
    }

    const toggleTodo = id => {
        todos = todos.map(todo => {
            if (todo.id === id) {
                return { ...todo, done: !todo.done }
            }
            return todo;
        });
    }

    $: categoryTodos = todos.filter(x => x.category == currentCategory);
</script>

<button on:click={toggleCategory}>Switch Categories</button>
<Todos todos={categoryTodos} {toggleTodo}>
</Todos>
Run Code Online (Sandbox Code Playgroud)

Todos.svelte:

<script>
    import { slide } from 'svelte/transition';
    export let todos;
    export let toggleTodo;

    $: complete = todos.filter(t => t.done);
    $: incomplete = todos.filter(t => !t.done);
</script>

<h1>Incomplete</h1>
<ul>
    {#each incomplete as {id, name} (id)}
    <li transition:slide|local on:click={() => toggleTodo(id)}>{name}</li>
    {/each}
</ul>
<h1>Complete</h1>
<ul>
    {#each complete as {id, name} (id)}
    <li transition:slide|local on:click={() => toggleTodo(id)}>{name}</li>
    {/each}
</ul>
Run Code Online (Sandbox Code Playgroud)

rix*_*ixo 7

更新

自 v3.28.0 起,此关键功能现已成为 Svelte 的一部分(请参阅问题)。

语法如下:

{#key expression}...{/key}
Run Code Online (Sandbox Code Playgroud)

上一个答案

在 React 中,您将使用keyprop 使渲染器重新创建一个可以重用的元素(相同的标签等)。

// React
<Todos items={items} key={currentCategory} />
Run Code Online (Sandbox Code Playgroud)

但是 Svelte 不支持key,是吗?嗯,有点。Svelte 确实具有等效功能,但仅限于{#each ...}块。

语法是这样的(文档——文档中没有提到这种精确的语法,但我想它只是被遗忘了):

{#key expression}...{/key}
Run Code Online (Sandbox Code Playgroud)

就像在 React 中一样,当键的值发生变化时,组件将被重新创建(否则会被重用)。

而且呜呜...

// React
<Todos items={items} key={currentCategory} />
Run Code Online (Sandbox Code Playgroud)

呼呼。对?

使用currentCategory作为重点将创建一个新的<Todos />组件每个cattegory发生变化时,这可能是你在你的情况下,想要的东西。

就像在 React 中一样,必须明智地选择键的值以在每次需要时重新创建,但不能更多(否则在您的情况下它会终止所需的项目间转换)。

键的值不限于每个循环中当前评估的项目。它可以来自 Svelte 范围内的任何地方,因此您可以发挥创意。它甚至可以是一个{}可以重新创建的内联对象......好吧,基本上一直都是!

编辑

您可以将 hack 变成它自己的组件,以便在消费者中使用更清晰的语法:

{#each expression as name (key)}...{/each}
Run Code Online (Sandbox Code Playgroud)

像这样使用:

<script>
  export let allItems
  export let currentCategory

  $: items = allItems.filter(x => x.category === currentCategory)
</script>

{#each [items] as todos (currentCategory)}
  <Todos {todos} />
{/each}
Run Code Online (Sandbox Code Playgroud)

请参阅Svelte 的 REPL 中的示例