svelte:如何声明两个循环反应变量?

Aus*_*ela 4 javascript svelte svelte-3 sveltekit

我有两个变量ab加起来为 100。如何设置反应式声明,以便 whena更改b为 be ,100 - a反之亦然?当我尝试类似的事情时

let total = 100;
$: a = total - b;
$: b = total - a;
Run Code Online (Sandbox Code Playgroud)

我收到“检测到循环依赖性”错误。有什么办法可以完成这件事吗?

rix*_*ixo 12

问题来自于这样一个事实:Svelte 希望按照反应块相互依赖的顺序对它们进行排序:它希望首先计算那些依赖但没有依赖关系的块,以避免不必要的计算......或者陷入困境环形。

此外,Svelte 考虑了反应式表达式中出现的任何反应式变量的依赖性。

因此,错误的实际来源是变量a和 都b出现在两个反应式表达式中。

解决方案是从反应式表达式中删除不需要的变量,即分配给的变量。这可以通过将分配移至反应块外部的函数来完成。不幸的是,它更冗长......

<script>
    let total = 100;
    let a = 0
    let b = 0
    
    const setA = () => {
        // the assignment to a (or b in the other function) are still
        // reactive, of course, but Svelte won't propagate changes to
        // variable that are assigned their current value, so this
        // will break the loop
        a = total - b
    }
    
    const setB = () => {
        b = total - a
    }
    
    $: setA(total - b);
    $: setB(total - a);
</script>

<pre>
    a: {a}
    b: {b}
</pre>

<label>
    a <input type="number" bind:value={a} />
</label>

<label>
    b <input type="number" bind:value={b} />
</label>
Run Code Online (Sandbox Code Playgroud)

编辑

正如 @avf 在评论中所说,上面的代码具有误导性,对于代码的读者来说并不那么明显。我当时这样写是因为我专注于演示 Svelte 反应性的原理。

在现实世界中,我宁愿推荐以下两种形式中的任何一种。

只要有可能,就倾向于显而易见:

// this function is just normal
const setA = (value) => {
  a = value
}

// this reactive expression makes it obvious and straightforward that 
// its dependencies are total and b, and only those
$: setA(total - b)
Run Code Online (Sandbox Code Playgroud)

当明显性不是一个选项时(例如,因为函数的内容比单纯的赋值更具副作用),我会依赖以下对于 Svelte 来说非常惯用的形式。不幸的是,对于那些通常不熟悉 Svelte 的人来说,它也非常晦涩……但我想这只是属于你必须学习才能真正精通该框架的事情。

const recomputeEverythingOrWhatever = () => {
  ...
}

// in Svelte lingo, this is broadly understood as "whenever a, or b, or
// total changes, then recompute everything (or whatever)"
$: a, b, total, recomputeEverythingOrWhatever()
Run Code Online (Sandbox Code Playgroud)

在我看来,这种语法很好,因为 Svelte 开发人员普遍熟悉并理解它。

但要小心在重新计算函数中输入的内容。如果它改变了响应式块的依赖项的值,这次代码将编译得很好,Svelte 甚至会在运行时中断更新循环以避免冻结 JS 线程......但是您最终可能会得到这样的代码:很难理解或预测。