svelte内部原理的响应机制
作者:互联网
使用svelte时,反应性会自动发生。在Svelte中,任何组件的单个片段都可以随时动态地重新生成!svelte精心管理每个片段(什么是一个片段?就是"代码段",是指svelte再合适的时间也就是依赖状态发生变化时,被动管理和重新执行的任何js表达式。最终,片段用于提供我们的html标记的动态,即它的反应性),根据依赖状态变化,根据需要监控和重新执行它们。
片段可以在两个地方使用
code-snippets
位于组件的js代码中(在
//下面代码 只要user对象更改(代码段的依赖项)name和phone就会重新变化
$: {name, phone} = user;
html-snippets
位于html标记中{…}这通常称为插值,这些操作是重量级的因为他们会导致html DOM的更改!
<p>Hello {name}</p>
<p>May we call you at {phone}</p>
术语:片段、代码片段和html
片段指的是svelte在其依赖状态更改时被动管理和调用的任何js表达式
片段可以是代码片段(反应式声明和反应式语句)
或html-snippets(插槽)
最终片段在我们的html标记中提供动态
反应触发器
svelte如何确定何时触发新执行我们的代码段?
svelte监听每个片段中引用的依赖状态,并在该状态发生变化时触发更新执行
svelte如何确定状态引用更改?
svete文档谈到"分配是反应性"和"svelte的反应性由分配触发" svelte通过赋值语义触发反应性(识别各种形式的赋值),这对于组件的本地状态是正确的。svelte编译器将识别赋值(以各种形式)并将复制的变量标记为已更改(即"过时")
对于赋值目标是原始对象还是对象(包括数组)存在很大区别
原始对象赋值
对于原始对象(字符串,数字,布尔等),仅当值发生变化时才会发生反应,它还结合了 JavaScript 身份语义(即priorState === nextState)
例如 myNum=(x+y)/2 只有当它的值实际发生变化才会被标记为"过时"如果先验证值10,计算结果为10 就不会发生变化
对象类型
事实证明,svelte中,您更改对象的任何内容整个对象都被标记为"过时",这包括本地组件对象,svelte对象存储,数组对象属性等。当改变对象会通知svelte对象已更改(通过将其分配给自身)
总结:依赖状态改变时触发代码段执行,原始类型仅在值更改的时候触发反应性,对象触发整个对象的反应性
诊断日志
简单诊断
//previous
<p>Hello {name}</p>
<p>May we call you at {phone}</p>
//current
<p>Hello {console.log('Name section fired) || name}</p>
<p>May we call you at {console.log('Phone section fired) || phone}</p>
高级诊断
//createReflectiveCounters.js
export defaule function createReflectiveCounter(logMsg){
const {subscribe,set,update} = writable(-1);
return {
subscribe,
monitor(...monitorDependents){
update((count)=>count+1);
logMsg&& console.log(logMsg);
return '';
},
reset:()=>set(0);
}
}
//调用
<i>{fooSectionReflexiveCount.monitor() || $foo}</i> //打印到工作台
<mark>{$fooSectionCount}:</mark> //直接展示到页面上
● html中调用
<script>
const fooReflexiveCount = createReflectiveCounter('foo section fired');
</script>
<!-- diagnostic reporter -->
<mark>{$fooReflexiveCount}:</mark>
<!-- monitor this section -->
<i>{fooReflexiveCount.monitor() || $foo}</i>
<!-- reset counts -->
<button on:click={fooReflexiveCount.reset}>Reset</button>
● 监视状态更改技术(代码片段中)
<script>
const fooChangeCount = createReflectiveCounter();
$: fooChangeCount.monitor($foo);
</script>
<!-- reporter/resetter -->
<i>$foo state change counts: {$fooChangeCount}</i>
<button on:click={fooChangeCount.reset}>Reset</button>
创建GreetUser.svelte
案例地址
t通过这个案例可以看出更改单个属性,html片段会重新执行,当点击Apply Change在没有属性更改的单机按钮,仍然会执行三个html片段,这个原因是因为使用了对象,对象引用有依赖项。如下所示,svelte将$user标记为过时时,因为任何引用对象的html片段都会重新执行。无论是否引用了.name
<p>Hello {$user.name}!</p>
svelte的创新渲染优化
以上案例证明,监听对象html会重新加载,但是这并不意味着会重新渲染dom,因为实际上加载dom会产生冗余和不必要的渲染。加载dom更新时昂贵的,在svelte内部重新执行了html-snippet,但是并不意味着导致DOM更新。
svelte实际上通过确保内容实际更改来优化DOM更新
增量更新
由于svelte只是内容实际更改才会触发dom name我们关注底部逻辑p() update这个方法,这是增量dom更新的方法。
//html
<p>Hello {$user.name}!</p>
//编译结果
//注释:逻辑位运算符 位与(&)、位或(|)、位异或(^)、非位(~)
//位运算就是对二进制数执行计算,是整数的逐位运算。例如,1+1=2,在十进制计算中是正确的,
//但是在二进制计算中,1+1=10;对于二进制数 100 取反,等于 001,而不是 -100。
p(ctx, [dirty]) {
// one of 3 sections ...
if (dirty & /*$user*/ 1 && // conditional Part I
t5_value !== (t5_value = /*$user*/ ctx[0].name + "")) { // conditional Part II
set_data(t5, t5_value); // the payload - update the DOM!
}
... snip snip
},
//set_data() 函数时一个实际更新DOM的svelte辅助使用程序
function set_data(text, data) {
data = '' + data;
if (text.data !== data)
text.data = data;
}
重点
● ctx[]数组包含我们所有的依赖项。 ctx[0]恰好是我们的
u
s
e
r
对
象
(
通
过
注
释
可
以
看
出
)
●
d
i
r
t
y
中
包
含
我
们
所
有
变
量
"
陈
旧
"
的
按
位
累
加
(
每
个
因
变
量
一
位
)
●
条
件
的
第
一
部
分
是
拉
出
user对象(通过注释可以看出) ● dirty中包含我们所有变量"陈旧"的按位累加(每个因变量一位) ● 条件的第一部分是拉出
user对象(通过注释可以看出)●dirty中包含我们所有变量"陈旧"的按位累加(每个因变量一位)●条件的第一部分是拉出user因变量的脏标志(使用位运算符&[using the bit-wise AND operator - &]) ,如果是,我们将继续第二部分(via the logical-AND operator - &&)
● 有条件的第二部分其实就是做两件事情,将最新的ts_value从我们的HTML代码段(将其转换为字符串后+’’)和它相比前/下一个判断的输出(使用标识语义!==).只有当上一个/下一个发生变化时,他才会执行条件负载(dom更新).最终这个条件是一个非常简单的原始字符串比较,
个人理解:被加入反应的对象,在属性被更新的时候内部的机制,是通过p也就是update的函数来做筛选更新内容的机制,方法会通过ctx[]数组存储我们所有的依赖项,例如KaTeX parse error: Expected 'EOF', got '&' at position 60: …程是第一看是否有dirty按位&̲//user 1的值 最后是重点 如果看html中的内容也就是t5_value=/$user/ ctx[0].name+"" 进行赋值,ts_value是否与最新的内容相同如果不同进行dom更新。
预解析变化
html中完成以"1-"开头
<i class:long-distance={phone.startsWith('1-')}>
... snip snip
</i>
这的问题是svelte将根据依赖项是否phone更改重新执行html-snippet,而不管css类是否会更改,在这里我们只改变后半部分,保留前半部分,这导致了更高数量的反射计数
解决方案
将逻辑条件移到代码片段中,生成html片段将导致更少的执行
//部分代码如下
$: classes = phone.startsWith('1-') ? 'long-distance' : '';
<i class="{probe2.monitor() || classes}">
{probe3.monitor() || phone}
</i>?
优化注意事项
这里有以下关于优化的"额外"需要考虑
洞察力:仅当活性成分发生反应时优化才相关
● 组件的初始渲染(安装时)将无条件执行它的所有html片段(无论依赖项或条件如何)
● 如果此后没有反应性,则无需关注这些优化技术
● 但要注意,反应性有事很难预测。此外,未来对您的代码的修订可能会引入反应性。所以要小心!
标签:片段,name,对象,响应,html,user,原理,svelte 来源: https://blog.csdn.net/qq_25122581/article/details/121530708