我在这里有点困惑,很不幸,我在Svelte的不和谐频道上找不到任何解决方案,所以我在这里...
我有两个类的相当基本的示例,让它们分别为App
和Comp
。
App
创建一个Comp
实例,然后在单击按钮后更新该实例的value
。
Comp实例应将此值设置为其他变量(inputValue
),并在更改该变量后应触发validate(inputValue)
这是反应性的。这是一个REPL:https://svelte.dev/repl/1df2eb0e67b240e9b1449e52fb26eb14?version=3.25.1
App.svelte:
<script>
import Comp from './Comp.svelte';
let value = 'now: ' + Date.now();
function clickHandler(e) {
value = 'now ' + Date.now();
}
</script>
<Comp
bind:value={value}
/>
<button type="button" on:click={clickHandler}>change value</button>
比较苗条:
<script>
import { onMount } from 'svelte';
export let value;
let rendered = false;
let inputValue = '';
$: validate(inputValue); // This doesn't execute. Why?
function validate(val) {
console.log('validation:', val);
}
onMount(() => {
rendered = true;
});
$: if (rendered) {
updateInputValue(value);
}
function updateInputValue(val) {
console.log('updateInputValue called!');
if (!value) {
inputValue = '';
}
else {
inputValue = value;
}
}
</script>
<input type="text" bind:value={inputValue}>
因此,一旦更改值:
if (rendered) {...}
条件称为updateInputValue
并更改inputValue
。 HTML输入元素将更新为该值。validate(inputValue)
从不对这一更改做出反应-为什么? 如果我在反应性updateInputValue
条件中省略了对if (rendered)
函数的额外调用,并将updateInputValue
函数体的代码直接置于条件中,则validate(inputValue)
被正确触发,即:
// works like this
$: if (rendered) {
if (!value) {
inputValue = '';
}
else {
inputValue = value;
}
}
那么在功能中更新后为什么不起作用?
答案 0 :(得分:4)
@johannchopin的answer揭示了这个问题。
您可以阅读my blogpost for slightly in-depth explanation of how reactive declaration works,这是tl; dr:
反应性声明批量执行。
svelte分批处理所有更改以在下一个更新周期中对其进行更新,并且在更新DOM之前,它将执行反应式声明以更新反应式变量。
反应性声明按其依赖项的顺序执行。
反应式声明是批量执行的,每个声明执行一次。一些声明是相互依赖的,例如:
let count = 0;
$: double = count * 2;
$: quadruple = double * 2;
在这种情况下,quadruple
取决于double
。因此,无论您的反应式声明的顺序是$: double = count * 2;
之前的$: quadruple = double * 2
还是相反,前者应在之前执行。
苗条的将按照相关性顺序对声明进行排序。
在彼此之间没有依赖关系的情况下:
$: validate(inputValue);
$: if (rendered) updateInputValue(value);
第一条语句取决于validate
和inputValue
,第二条语句取决于rendered
,updateInputValue
和value
,声明按其顺序保留是。
现在,了解反应式声明的这两种行为,让我们来看看您的REPL。
在更改inputValue
,rendered
或value
时,Svelte将批量更改,并开始新的更新周期。
在更新DOM之前,Svelte会一口气执行所有反应式声明。
由于validate(inputValue);
和if (rendered) updateInputValue(value);
语句之间没有依赖关系(如前所述),它们将按顺序执行。
如果仅更改rendered
或value
,将不会执行第一条语句(validate(inputValue)
),同样,如果更改inputValue
,第二条语句({ {1}})将不会执行。
现在,在if (rendered) updateInputValue(value)
中,您可以更改updateInputValue
的值,但是由于我们已经处于更新周期,因此我们不会启动新的值。
这通常不是问题,因为如果我们按照相关性顺序对反应式声明进行排序,则更新依赖变量的语句将在依赖变量的语句之前执行。
因此,知道出了什么问题后,您可以寻求一些“解决方案”。
在此REPL
中查看对反应式声明进行排序的区别因此将您的REPL更改为:
inputValue
$: if (rendered) {
updateInputValue(value);
}
$: validate(inputValue);
答案 1 :(得分:1)
真的很奇怪(我无法真正解释它),但是如果您将反应式语句ERROR: column "delta_sec" does not exist
LINE 5: where delta_sec > 10000
^
SQL state: 42703
Character: 125
放在函数$: validate(inputValue);
声明之后,它将按预期工作:
updateInputValue
选中此REPL。
答案 2 :(得分:0)
以下是原始帖子问题和一些变通方法的最小示例:
<script>
let nb
let n=0
$: console.log("nb1:",nb)
$: update(n)
$: console.log("nb2:",nb)
function update(v) {
console.log("update",v)
nb = v
}
</script>
<h1 on:click={()=>n=Math.floor(Math.random()*100)}>click: {nb}</h1>
<h1 on:click={()=>update(Math.floor(Math.random()*100))}>click: {nb}</h1>
为nb
分配了新值并且由于分配而运行n
时,不会触发$: update(n)
的第一个日志。 nb
的第二个日志正在运行,因为它是在分配给nb
之后。
如果直接调用update
函数(最后一个h1
),一切正常。
如果您在最高级别为nb
分配了一个值,那么一切也将起作用:
<script>
let nb
let n=0
$: console.log("nb1:",nb)
$: nb = update(n)
$: console.log("nb2:",nb)
function update(v) {
console.log("update",v)
return v
}
</script>
<h1 on:click={()=>n=Math.floor(Math.random()*100)}>click: {nb}</h1>
<h1 on:click={()=>update(Math.floor(Math.random()*100))}>click: {nb}</h1>
我相信这是一个错误。