我正在构建一个应用程序,用户可以在其中添加和删除不同“类型”的项目。例如,可能存在用户可以编辑文本的文本类型和用户可以放入图像的图像类型。

在列表中添加项目将导致列表更新并显示所有旧项目以及新项目。同样,删除项目将删除该项目,并使列表仅显示其余项目。

添加功能可以很好地工作。当项目属于不同类型时,删除会带来问题。这是我的(简化)代码:

// List.svelte

<script>
    import {writable} from "svelte/store";
    import Wrapper from "./Wrapper.svelte";

    const items = [
        {
            type: "text",
            value: "Test 1"
        },
        {
            type: "img",
            value: "https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif"
        }
    ];

    const listStore = writable(items);
</script>

{#each $listStore as item, i}
    <Wrapper item={item} store={listStore} myIndex={i} />
{/each}


// Wrapper.svelte

<script>
    import {onMount} from "svelte";

    export let item;
    export let store;
    export let myIndex;

    let DynamicItem;

    const handleDeleteItem = () => {
        store.update((items) => ([...items.slice(0, myIndex), ...items.slice(myIndex + 1)]))
    }

    onMount(async () => {
        let imported;

        if(item.type === "text") {
            imported = await import("./TextItem.svelte")
        } else {
            imported = await import("./ImgItem.svelte")
        }

        DynamicItem = imported.default;
    })
</script>

<svelte:component this={DynamicItem} data={item} />
<button on:click={handleDeleteItem}>Delete</button>


// TextItem.svelte

<script>
    export let data;
</script>

<div contenteditable="true" bind:textContent={data.value} />


// ImgItem.svelte

<script>
    export let data;
</script>

<img src="{data.value}" width="100px" height="100px" alt="img alt" />


当用户通过单击第一个删除的TextItem从列表中删除button时,ImgItem不再显示图像,而是仅将src属性呈现为文本。

查看HTML输出时,在执行删除操作之前,它看起来像这样:

<body>
    <div contenteditable="true">Test 1</div>
    <button>Delete</button>
    <img src="https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif" alt="img alt" width="100px" height="100px">
    <button>Delete</button>
</body>


然后,它看起来像这样:

<body>
    <div contenteditable="true">https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif</div>
    <button>Delete</button>
</body>


因此,看起来Svelte并不是真正删除TextItem而是将内容从下一项移到其位置。

如何修复我的代码,以便真正删除项目?目标应该是通过执行相同的操作来产生以下HTML:

<body>
    <img src="https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif" alt="img alt" width="100px" height="100px">
    <button>Delete</button>
</body>


任何帮助表示赞赏。谢谢

最佳答案

因此,看起来Svelte并没有真正删除TextItem,而是将内容从下一个项目移到了它的位置。


是的,绝对是这样。

Svelte(像React一样)仅在其性质明显改变(例如,标记名称不同)时才在给定位置重新创建元素。否则,可以认为它是相同的元素/组件,只是某些道具/状态发生了变化。

为了提示框架元素的标识已更改,您必须使用key功能。在React中,它是一个文字key道具。在Svelte中,仅在{#each ...}块(for now?)中支持键。语法如下(docs):

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

# also works, I think it is an oversight in the docs currently
{#each expression as name, (key)}...{/each}


key的值对于每个项目都必须唯一,才能起作用。通常,您会使用排序ID。但实际上,它可以是任何东西。如果元素/组件在给定位置的值更改,则将重新创建它。

因此,根据您的情况,可以通过使用项目类型作为键来解决问题,例如:

{#each $listStore as item, i (item.type)}
    <Wrapper item={item} store={listStore} myIndex={i} />
{/each}


这可以解决您要询问的问题,但是我必须说这不是一个好主意。出于其他原因,您最好将真实的唯一ID添加到商品中,而改用它们。使用上面的代码,相同类型的项目将继续共享DOM元素,而这可能并不是您想要的。例如,您可能对DOM元素(如输入)的焦点或内部状态(值)有疑问。

因此,类似这样的方法更好,并且可能是解决方法:

<script>
    import {writable} from "svelte/store";
    import Wrapper from "./Wrapper.svelte";

    const items = [
        {
            id: 1,
            type: "text",
            value: "Test 1"
        },
        {
            id: 2,
            type: "img",
            value: "https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif"
        }
    ];

    const listStore = writable(items);
</script>

{#each $listStore as item, i (item.id)}
    <Wrapper item={item} store={listStore} myIndex={i} />
{/each}

关于javascript - Svelte.js-无法从列表中正确删除项目,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59080722/

10-14 14:38
查看更多