You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Svelte中Todo项编辑按钮失效问题排查及修复请求

问题:Svelte中Todo项Edit按钮分离函数后失效的原因及修复方案

在以下Svelte代码中,单个Todo项的Edit按钮绑定到独立的editTodo函数后无法正常工作,该函数本应将isEdited属性设为true。但将逻辑内联写在on:click(如<button on:click={() => isEdited = true}>)时却能正常运行,这是为什么?如何修改代码让分离的函数生效?


失效原因

这是JavaScript基本类型的值传递特性导致的:isEdited是布尔值(属于基本类型),当你把它作为参数传给editTodo时,传递的是值的副本,而非原Todo对象的属性引用。函数内执行isEdited = true只是修改了这个副本,完全不会影响原Todo对象里的isEdited属性,因此无法触发Svelte的响应式更新。

而内联写法中,isEdited = true直接操作的是原Todo对象的属性(#each解构时的isEdited是原对象属性的绑定引用),所以能正确修改并触发视图更新。


修复方案

要修改原Todo对象的属性,需将整个Todo对象作为参数传入editTodo函数,利用对象的引用类型特性,直接修改对象的isEdited属性,这样就能触发Svelte的响应式更新。

修改后的完整代码:

<script>
    let todos = [
        {
            desc: 'Get some groceries for cooking',
            isDone: false,
            isEdited: false,
        },
        {
            desc: 'Wash the dishes and clean the house',
            isDone: false,
            isEdited: false,
        },
    ];
    
    let newTodo;
    function createTodo() {
        todos = [...todos, {desc: newTodo, isDone: false, isEdited: false}];
        newTodo = null;
    }
    
    // 修改函数,接收整个todo对象
    function editTodo(todo) {
        todo.isEdited = true;
    }
    
    function deleteTodo(desc) {
        todos = todos.filter(todo => todo.desc !== desc);
    }
    
    $: console.log(todos);
</script>

<h1>
    Todoapp
</h1>
<form on:submit|preventDefault={createTodo}>
    <label for='create-todo' class='create-todo-label'>
        Create a new todo:
    </label>
    <input bind:value={newTodo} type='text' placeholder='Do something' id='create-todo' required />
    <button>
        Create
    </button>
</form>

{#if todos.length !== 0}
    <h2>
        Your todos:
    </h2>
{/if}

<ul>
    <!-- 修改each循环,直接使用todo对象引用 -->
    {#each todos as todo, index}
        <li>
            <label for='{index}' class:done={todo.isDone}>
                {todo.desc}
            </label>
            <input id='{index}' bind:checked={todo.isDone} type='checkbox' />
        </li>
    
        <!-- 点击时传递整个todo对象 -->
        <button on:click={() => editTodo(todo)}>
            Edit
        </button>
        {#if todo.isEdited}
            <textarea cols='30' rows='5' bind:value={todo.desc} />
        {/if}
    
        <button on:click={() => deleteTodo(todo.desc)}>
            Delete
        </button>
    {/each}
</ul>

<style>
    .create-todo-label {
        margin-block-end: 0.25rem;
    }
    
    label {
        max-inline-size: fit-content;
    }
    
    label.done {
        text-decoration: line-through;
    }
</style>

补充说明

如果希望保留原有的解构写法,也可以在#each中同时获取整个todo对象:

{#each todos as todo, index}
    <!-- 解构属性的同时保留todo引用 -->
    {#const { desc, isDone, isEdited } = todo}
    <!-- 后续代码可以继续使用desc、isDone、isEdited,同时传递todo给editTodo -->
    <button on:click={() => editTodo(todo)}>Edit</button>
{/each}

内容的提问来源于stack exchange,提问作者Thilo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 16:18:34