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

svelte: how can I declare two cyclically reactive variables?

我有两个变量 ab,它们加起来是 100。我如何设置反应式声明,以便当 a 更改时 b 更改为是 100 - a 反之亦然?当我尝试

let total = 100;
$: a = total - b;
$: b = total - a;

我收到 'Cyclical dependency detected' 错误。有什么办法可以做到吗?

那不行; Svelte 不允许这样,这个循环也无法解决。如果您希望变量可编辑,则不能将其声明为反应式 ($: x = ...)。您可以使用反应性 statements/blocks 反应性地设置常规变量,也可以改用事件(参见其他答案)。

下面解释一下为什么逻辑上也无法解决

将其视为具有两个未知数的两个方程式,您会得到这种无用的简化:

a = total - b
b = total - a
b = total - (total - b)
b = total - total + b
b = b

您必须至少修复其中一个值,否则所有可能的值都在这里有效。


您也可以先将方程归一化:

a = total - b => a + b = total => a + b = total
b = total - a => b + a = total => a + b = total

如你所见,它们是相同的,所以你实际上有两个未知数和一个方程,所以这是未指定的。

(请注意,即使这会产生有效的解决方案,Svelte 也无法为您求解线性方程组。)

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

此外,Svelte 认为反应式表达式的依赖性是其中出现的任何反应式变量。

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

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

<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>

除非您需要从程序更改中更新值,否则我会完全放弃反应式语句并改为使用事件:当用户调整一个变量时,更新另一个。

<script>
  const total = 100;
  let a = total;
  let b = 0;
</script>

a: {a} <br>
b: {b}

<label>
  a <input type="number" bind:value={a}
           on:input={() => b = total - a} />
</label>

<label>
  b <input type="number" bind:value={b}
           on:input={() => a = total - b} />
</label>