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

Svelte如何响应数据存储中字典变量的变更?

问题描述

我正尝试让我的Svelte应用响应数据存储(data-store)中定义的字典的变更。创建新条目时会调用createNewItem函数,该函数内的日志显示user store已更新,但尽管user.options已明确变更,getOptions函数却未被调用。

代码示例

App.svelte

<select bind:value={selectedOption}>
{#each user_options as option}
    <option value={option}>{option}</option>
{/each}
</select>

<button on:click={addNewItem}>New item</button>
<NewItem />

<script>
    import {user, state, elementDisplay } from './data'
import NewItem from "./NewItem.svelte";
    
$: user_options = getOptions($user.options);
$: new_item = createNewItem($state.new_item);
    
let selectedOption = "";
    
    function getOptions(user_options) {
        console.log('user changed')
        let options = []
        if (state.new_item != '') {
            user_options[$user.new_item] = ''
        }
        for (const [option, value] of Object.entries(user_options)) {
            options.push(option)
        }
        return options
    }
    
    function createNewItem(new_item) {
            if (new_item.length > 0 ) {
                console.log('ante', $user)
                $user.options[new_item] = '';
                console.log('post', $user)
            }
        }

function addNewItem() {
        elementDisplay('new-item', 'block')
        document.getElementById('new-item-value').focus();
    }
</script>

NewItem.svelte

<div id="new-item">
    <input type="text" id="new-item-value">
    <div class="buttons">
        <button class="select-button" on:click={useItem}>Use</button>
        <button class="select-button" on:click={close}>Close</button>
    </div>
</div>

<style>
    #new-item {
        display: none;
    }
</style>

<script>
    import { state, elementDisplay } from './data'

    function useItem() {
        let input_field = document.getElementById('new-item-value')
        if (input_field.value) {
            $state.new_item = input_field.value
        }
        close()
    }

    function close() {
            elementDisplay('new-item', 'none');
        }
</script>

data.js

import { writable } from 'svelte/store';

export let user = writable({
    new_item: '',
    options: {
    "2": "Option 2",
    "1": "Option 1",
    "3": "Option 3",
        }
    }
);

export let state = writable({
    new_item: '',
});

export function elementDisplay(item_name, display) {
    let item = document.getElementById(item_name);
    item.style.display = display;
};
问题原因与解决方案

核心问题

Svelte的writable store仅在引用发生变化时才会触发订阅更新。你在createNewItem里直接修改$user.options的属性:

$user.options[new_item] = '';

这属于原地修改对象,user store的引用没有改变,因此不会触发依赖$user的反应式语句(也就是$: user_options = getOptions($user.options))执行,自然getOptions不会被调用。

修复方案

你需要创建新的对象来替换原有的user对象,让store检测到引用变化:

方案1:使用展开运算符重新赋值

修改createNewItem函数:

function createNewItem(new_item) {
    if (new_item.length > 0 ) {
        console.log('ante', $user)
        // 创建新的options对象,添加新条目
        const newOptions = { ...$user.options, [new_item]: '' };
        // 创建新的user对象替换原对象
        $user = { ...$user, options: newOptions };
        console.log('post', $user)
    }
}

方案2:使用store的update方法

也可以直接调用store的update方法,在回调中返回新的状态:

function createNewItem(new_item) {
    if (new_item.length > 0 ) {
        console.log('ante', $user)
        user.update(currentUser => {
            return {
                ...currentUser,
                options: { ...currentUser.options, [new_item]: '' }
            };
        });
        console.log('post', $user)
    }
}

额外优化

在getOptions函数中,你直接修改了传入的user_options参数(也就是$user.options的引用),这会导致store里的对象被原地修改,同样可能引发更新问题。建议创建新数组时不要修改原对象:

function getOptions(user_options) {
    console.log('user changed')
    let options = [];
    // 创建新的options对象,避免修改原store数据
    const tempOptions = { ...user_options };
    if ($state.new_item != '') {
        tempOptions[$state.new_item] = '';
    }
    for (const [option] of Object.entries(tempOptions)) {
        options.push(option);
    }
    return options;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 11:31:11