其他分享
首页 > 其他分享> > svelte内部原理的响应机制

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