当 Svelte 重用父 dom 元素时如何确保仅本地转换 [英] How to ensure local-only transitions when Svelte is reusing the parent dom element

查看:24
本文介绍了当 Svelte 重用父 dom 元素时如何确保仅本地转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In Svelte, I have a component which is used to display items in two different lists. When those items are moved from one list to the other, they use a transition to animate in or out.

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

However, I also have a way to filter what is displayed on the screen. Displaying a new set of items would use the same component, but with different data. In this case, I don't want the transition animation to occur. I assumed that adding the local modifier would do the trick, but it seems that Svelte isn't dropping the parent element to the list, but instead reusing it and adding the new data in the existing list DOM element.

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

I've tried to reproduce what I'm seeing in the sample code below.

想要的行为:

  1. 单击待办事项会将待办事项从一个列表切换到另一个列表.
  2. 点击切换类别"将切换列出的 TODO,而不会对添加或删除的 TODO 的
  3. 进行动画处理.
  1. Clicking on a TODO will toggle the TODO from one list to the other.
  2. Clicking "Switch Categories" will switch which TODOs are listed, without animating the <li>s of the TODOs that are added or removed.

实际行为:

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

如何更改我的示例以便获得我想要的效果?

How can I change my example so that I get the effect that I want?

App.svelte:

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>

Todos.svelte:

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>

推荐答案

更新

自 v3.28.0 以来,此关键功能现已成为 Svelte 的一部分(请参阅 issue).

语法如下:

{#key expression}...{/key}


上一个回答

在 React 中,您可以使用 key 属性让渲染器重新创建一个可以重用的元素(相同的标签等).


Previous answer

In React, you would use the key prop to make the renderer recreates an element that could have been reused (same tag, etc.).

// React
<Todos items={items} key={currentCategory} />

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

But Svelte doesn't support key, does it? Well, somewhat. Svelte does have an equivalent feature, but only in {#each ...} blocks.

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

The syntax is this (docs -- this precise syntax is not mentioned in the docs, but I guess it has just been forgotten):

{#each expression as name (key)}...{/each}

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

Like in React, the component will be recreated when the value of the key changes (and reused otherwise).

而且太...

<script>
  export let allItems
  export let currentCategory

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

{#each [items] as todos (currentCategory)}
  <Todos {todos} />
{/each}

呼呼.对吗?

使用 currentCategory 作为键将在每次类别更改时创建一个新的 <Todos/> 组件,这可能是您想要的.

Using currentCategory as a key will create a new <Todos /> component each time the cattegory changes, which is probably what you want in your case.

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

Like in React, the value of the key must be chosen wisely to recreate every time is needed, but not more (or it would kill the desired inter-item transition in your case).

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

The value of the key is not limited to the currently evaluated item in the each loop. It can come from anywhere in the scope in Svelte, so you can get creative. It could even be an inline object {} which would recreate... Well, basically all the time!

编辑

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

You can turn the hack into its own component, for cleaner syntax in the consumers:

<!-- Keyed.svelte -->

<script>
    export let key
    // we just need a 1-length array
    const items = [0]
</script>

{#each items as x (key)}
    <slot />
{/each}

像这样使用:

<script>
  import Keyed from './Keyed.svelte'
  
  export let items
  export let category
</script>

<Keyed key={category}>
  <Todos {items} />
</Keyed>

参见 Svelte 的 REPL 中的示例.

See example in Svelte's REPL.

这篇关于当 Svelte 重用父 dom 元素时如何确保仅本地转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆