ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

svelte内部原理的响应机制

2021-11-25 10:00:13  阅读:168  来源: 互联网

标签:片段 name 对象 响应 html user 原理 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有