javascript - 当函数中的变量更改时,不会触发 Svelte 反应性
问题描述
我在这里有点困惑,不幸的是我无法在 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;
}
}
那么为什么在函数中更新它不起作用呢?
解决方案
@johannchopin 的回答揭示了这个问题。
您可以阅读我的博客文章,以稍微深入地解释反应式声明的工作原理,这里是 tl;dr:
反应式声明是批量执行的。
svelte 批处理所有更改以在下一个更新周期中更新它们,并且在更新 DOM 之前,它将执行响应式声明以更新响应式变量。
反应式声明按照它们的依赖顺序执行。
反应式声明是批量执行的,每个声明执行一次。一些声明相互依赖,例如:
let count = 0; $: double = count * 2; $: quadruple = double * 2;
在这种情况下,
quadruple
取决于double
。因此,无论您的反应性声明的顺序如何,$: double = count * 2;
之前$: quadruple = double * 2
或相反,前者应该并且将在后者之前执行。Svelte将按照依赖顺序对声明进行排序。
在彼此没有依赖关系的情况下:
$: validate(inputValue); $: if (rendered) updateInputValue(value);
第一个语句取决于
validate
andinputValue
,第二个语句取决于rendered
,updateInputValue
andvalue
,声明保持原样。
现在,了解了反应式声明的这两种行为,让我们来看看你的REPL。
当您更改或时inputValue
,Svelte 将批量更改,并开始新的更新周期。rendered
value
在更新 DOM 之前,Svelte 将一次性执行所有的反应式声明。
validate(inputValue);
因为and语句之间没有依赖关系if (rendered) updateInputValue(value);
,(如前所述),它们将按顺序执行。
如果您更改rendered
或value
仅更改,validate(inputValue)
则不会执行第一个语句(),同样,如果您更改inputValue
第二个语句(if (rendered) updateInputValue(value)
),则不会执行。
现在,在updateInputValue
您更改 的值inputValue
,但因为我们已经处于更新周期,我们不会开始一个新的。
这通常不是问题,因为如果我们按照依赖顺序对响应式声明进行排序,更新所依赖变量的语句将在依赖变量的语句之前执行。
因此,知道出了什么问题,您可以寻求一些“解决方案”。
- 手动重新排序反应式声明语句,尤其是在执行顺序存在隐式依赖时。
请参阅此REPL中订购反应式声明的区别
因此,将您的 REPL 更改为:
$: if (rendered) {
updateInputValue(value);
}
$: validate(inputValue);
- 在响应式声明中显式定义依赖关系
$: validate(inputValue);
$: if (rendered) {
inputValue = updateInputValue(value);
}
function updateInputValue(val) {
console.log('updateInputValue called!');
if (!value) {
return '';
}
else {
return value;
}
}
推荐阅读
- r - 将函数应用于列 - 维基百科检索数据
- php - 如何从 IIS 导出所有站点及其 PHP 版本
- java - 如何通过多个属性找到重复的对象并将它们合并?
- javascript - 如何改善 ReactJS 中的设置状态?
- apache-spark - 动态分区剪枝不清楚
- java - 结合 tomcat 和 postgres 时 Docker 映像不运行
- excel - 如何找到描述我的数据行为的函数?
- scala - Scala 中的 MATCH_RECOGNIZE 示例
- grails - 在 grails 3.3.8 中禁用 SuffixPatternMatch
- javascript - 角材料 dialogRef.afterClosed().subscribe 不处理错误