svelte 中的自引用拓扑执行顺序
Self-referencing topological execution order in svelte
在 svelte 中,反应块拓扑执行顺序如何与自引用块(/改变它们引用的反应变量的块)一起工作?
例如,如果我这样做:
<script>
export let a = [];
export let b = [];
// Merge produces a sorted array, assuming that a and b are sorted
import { merge } from './utils.js';
$: combined = merge(a,b);
$: a = [...a].sort();
$: b = [...b].sort();
</script>
{#each combined as item}
<p>{item}</p>
{/each}
a
和 b
是否保证在合并之前排序?它似乎按预期工作。规则是什么?这在某处记录了吗?
一个更完整的交互式示例 UI:https://svelte.dev/repl/24135c927ca14186b1884c47a851e471?version=3.23.2
编辑: 更改了声明顺序以使行为更清晰
如果需要,Svelte 会分析响应语句并对其进行排序。它必须这样做,因为有些变量可能依赖于多个变量,这些变量是反应性的,并且它必须确保所有变量都正确更新。
我已经稍微更新了你的 REPL,这样我们就可以监视生成的 $$self.$$.update
方法:https://svelte.dev/repl/28ad4789488f449f903e1165c1134e54?version=3.23.2
我已经添加了一些代码(永远不要在生产中使用,因为它会混淆 svelte 内部结构)以向更新的方法添加日志语句:
onMount(() => {
// get the component
const self = eval('$$self');
// store the update method
const update = self.$$.update
// replace the update method with one that logs some informatin and calls the original one
self.$$.update = () => {
console.log('update called, dirty=', self.$$.dirty)
update()
}
})
并添加一个点击按钮,否则 svelte 会在我们的更新到位之前计算所有内容。
我们现在看到的是:
"sorted b"
"sorted a"
"combined"
"update called, dirty="
▶
Array(1)[ 6 ]
"sorted b"
"sorted a"
"combined"
- svelte 将具有
a
和 b
的组件安装为空数组
- 然后我们点击按钮(我们的更新现在已经打好了猴子补丁并准备就绪)
- update 被调用一次,脏值为 6,即 2 + 4,这意味着
a
(2) 和 b
(4) 现在是脏的。我们通过点击按钮来做到这一点
- svelte 再次运行更新方法
$$self.$$.update = () => {
if ($$self.$$.dirty & /*b*/ 4) {
$: {
$$invalidate(2, b = [...b].sort());
console.log("sorted b");
}
}
if ($$self.$$.dirty & /*a*/ 2) {
$: {
$$invalidate(1, a = [...a].sort());
console.log("sorted a");
}
}
if ($$self.$$.dirty & /*a, b*/ 6) {
$: {
$$invalidate(0, combined = merge(a, b));
console.log("combined");
}
}
};
Svelte 认识到,带有 merge
的反应语句同时依赖于 a
和 b
并对条件列表进行排序,以便它在 after a
和 b
的更新。 a 和 b 的顺序无关紧要,它们不相互依赖。
因此,作为一个结论:是的,svelte 通过编译正确的更新方法和我们声明反应语句的顺序来保证合并是在排序列表上完成的(在这个例子中),这无关紧要。
至少不是为了苗条。当然,它是为了提高可读性。
在 svelte 中,反应块拓扑执行顺序如何与自引用块(/改变它们引用的反应变量的块)一起工作?
例如,如果我这样做:
<script>
export let a = [];
export let b = [];
// Merge produces a sorted array, assuming that a and b are sorted
import { merge } from './utils.js';
$: combined = merge(a,b);
$: a = [...a].sort();
$: b = [...b].sort();
</script>
{#each combined as item}
<p>{item}</p>
{/each}
a
和 b
是否保证在合并之前排序?它似乎按预期工作。规则是什么?这在某处记录了吗?
一个更完整的交互式示例 UI:https://svelte.dev/repl/24135c927ca14186b1884c47a851e471?version=3.23.2
编辑: 更改了声明顺序以使行为更清晰
如果需要,Svelte 会分析响应语句并对其进行排序。它必须这样做,因为有些变量可能依赖于多个变量,这些变量是反应性的,并且它必须确保所有变量都正确更新。
我已经稍微更新了你的 REPL,这样我们就可以监视生成的 $$self.$$.update
方法:https://svelte.dev/repl/28ad4789488f449f903e1165c1134e54?version=3.23.2
我已经添加了一些代码(永远不要在生产中使用,因为它会混淆 svelte 内部结构)以向更新的方法添加日志语句:
onMount(() => {
// get the component
const self = eval('$$self');
// store the update method
const update = self.$$.update
// replace the update method with one that logs some informatin and calls the original one
self.$$.update = () => {
console.log('update called, dirty=', self.$$.dirty)
update()
}
})
并添加一个点击按钮,否则 svelte 会在我们的更新到位之前计算所有内容。
我们现在看到的是:
"sorted b"
"sorted a"
"combined"
"update called, dirty="
▶
Array(1)[ 6 ]
"sorted b"
"sorted a"
"combined"
- svelte 将具有
a
和b
的组件安装为空数组 - 然后我们点击按钮(我们的更新现在已经打好了猴子补丁并准备就绪)
- update 被调用一次,脏值为 6,即 2 + 4,这意味着
a
(2) 和b
(4) 现在是脏的。我们通过点击按钮来做到这一点 - svelte 再次运行更新方法
$$self.$$.update = () => {
if ($$self.$$.dirty & /*b*/ 4) {
$: {
$$invalidate(2, b = [...b].sort());
console.log("sorted b");
}
}
if ($$self.$$.dirty & /*a*/ 2) {
$: {
$$invalidate(1, a = [...a].sort());
console.log("sorted a");
}
}
if ($$self.$$.dirty & /*a, b*/ 6) {
$: {
$$invalidate(0, combined = merge(a, b));
console.log("combined");
}
}
};
Svelte 认识到,带有 merge
的反应语句同时依赖于 a
和 b
并对条件列表进行排序,以便它在 after a
和 b
的更新。 a 和 b 的顺序无关紧要,它们不相互依赖。
因此,作为一个结论:是的,svelte 通过编译正确的更新方法和我们声明反应语句的顺序来保证合并是在排序列表上完成的(在这个例子中),这无关紧要。
至少不是为了苗条。当然,它是为了提高可读性。