Svelte 反应性如何在函数内部工作?

Posted

技术标签:

【中文标题】Svelte 反应性如何在函数内部工作?【英文标题】:How does Svelte reactivity work inside functions? 【发布时间】:2020-01-18 02:57:38 【问题描述】:

我在 Svelte 的反应性方面存在一些严重问题。我已经隔离了我认为至少是我的主要问题之一。将变量绑定到复选框时,在函数内部而不是外部设置变量时反应性似乎中断。这是预期的行为吗?在那种情况下为什么?预期的工作流程是什么?

示例代码,一个 Svelte 组件:

<script>
    let foo = true;

    // This assignment works both on the plain text view (Foo: true/false)
    // and on the checkbox
    // setInterval(() => foo = !foo, 500)

    // This assignment works only on the plain text view (Foo: true/false)
    // but not for the checkbox
    function action() 
        foo = !foo;
    
</script>

Foo:  foo <br />
Checkbox: <input type="checkbox" bind:checked= foo  on:click|preventDefault= action  />

Svelte REPL 来看看这个问题的实际效果:https://svelte.dev/repl/73de7d705ab3442786710cd88ea3f625?version=3.12.1

【问题讨论】:

【参考方案1】:

最好的解决方案要简单得多,因为双向绑定本身就可以满足您的所有需求。所以,这样做:

<script>
  let foo = true;
</script>

Foo:  foo <br />
Checkbox: <input type="checkbox" bind:checked= foo  />

我知道这个问题已经存在两年了,但我认为它需要更好地结束。

【讨论】:

【参考方案2】:

我建议不要使用 preventDefault 取消事件,因为它会停止复选框的默认行为。

因此reactivitybroken。如果您将 foo 的重新分配包装在 setTimeout 中,您只会欺骗事件循环并且不会阻止默认值。

因此,如果您想停止事件的传播,请根据 Filip 的建议使用 preventDefault。

    <script>
        let foo = true;

        // This assignment works both on the plain text view (Foo: true/false)
        // and on the checkbox
        // setInterval(() => foo = !foo, 500)

        // This assignment works only on the plain text view (Foo: true/false)
        // but not for the checkbox
        async function action(e) 
                e.preventDefault();
                setTimeout(() => foo =! foo);

        
    </script>

    Foo:  foo <br />
    Checkbox: <input type="checkbox" bind:checked= foo  on:click= action  />

【讨论】:

preventDefault 是必需的,因为例如在函数中完成异步操作之前状态不应更改。很遗憾,它并没有解决我的问题,但谢谢。 :) 我已对您的建议进行了答复。以便在异步操作中处理状态更改并阻止默认值 嗯,我尝试在 REPL 中移动 preventDefault 但也无法使其正常工作? svelte.dev/repl/f6fefe4f1656429d980d964c1ff25b53?version=3.12.1 (不同之处在于我在操作中添加了 e:所以 preventDefault() 实际运行)【参考方案3】:

似乎您的问题是由bind: 选项引起的。这样做会导致 foo 被更新两次:一次用于“动作”,一次是在绑定启动时在幕后。

只需删除 bind: 即可

<script>
    let foo = true;

    // This assignment works both on the plain text view (Foo: true/false)
    // and on the checkbox
    // setInterval(() => foo = !foo, 500)

    // This assignment works only on the plain text view (Foo: true/false)
    // but not for the checkbox
    function action() 
        setTimeout(() => foo = !foo, 1000);
    
</script>

Foo:  foo <br />
Checkbox: <input type="checkbox" checked= foo  on:click|preventDefault= action  />

这是有道理的,因为您确实希望将选中状态双向绑定到 foo。您只希望复选框反映 foo 的值。

【讨论】:

代码示例有效,但诀窍在于 setTimeout()。即使我添加了 bind: back,它也可以工作,只要 foo 声明在该超时内而不是直接在函数第一范围内。【参考方案4】:

如果您将变量声明包装在 setTimeout 中而没有任何时间限制(使其默认为 0 并因此尽快运行),您的代码将起作用。

function action() 
    setTimeout(() => 
        foo = !foo;
    );

我希望我能解释为什么会这样,但我不能......

【讨论】:

以上是关于Svelte 反应性如何在函数内部工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Svelte 的类属性中使用反应函数?

如何触发/强制更新 Svelte 组件

Svelte:双向绑定触发反应性两次

Svelte:当组件更改数据时存储数据不响应,反之亦然

Vue.js 反应性如何在幕后工作?

TypeScript:如何使函数具有反应性