vue3 响应式,vue2.0响应式原理

  vue3 响应式,vue2.0响应式原理

  与之前的版本相比,Vue3.0更快、更小、更易维护、更接近原生、对开发者更友好。本文详细介绍了Vue3.0,有需要了解的朋友可以参考本文。

  

目录

  使用与case reactive API相关的进程创建一个响应式对象mutableHandlers处理函数get函数调用时间轨迹集合依赖于set函数触发分布依赖于get和副作用渲染函数关联副作用渲染函数在过滤的最后,我们知道Vue 2.0通过使用Ojbect.defineProperty劫持了对对象已有属性值的读取和修改,但是这个API无法监控对象属性的添加和删除,此外,为了深度劫持对象的内部属性,需要在初始化时对内部属性递归调用Ojbect.defineProperty,这导致了性能消耗。为了解决这些问题,Vue 3.0通过使用代理重写了响应逻辑并优化了相关性能。

  

使用案例

  我们举个例子,看看Vue 3.0的响应式API是怎么写的:

  Changeperson可以改变响应数据person的值,person的改变会触发组件重新渲染更新DOM

  这里我们可以看到,在Vue 3.0的使用中,开发者使用了reactive函数来确定哪些数据是响应式的,这样就可以避免一些不必要的响应式性能消耗。例如,在这种情况下,我们不需要生成nowIndex响应数据。(当然Vue 2.0也可以定义数据函数之外的数据,也是无响应数据)

  我们来看看无功函数的实现原理!

  

reactive API相关的流程

  

reactive

  代码说明:

  1.如果目标对象target是readonly对象,则直接返回目标对象,因为readonly对象不能设置为responsive对象。

  2.调用createReactiveObject函数继续该过程。

  

createReactiveObject 创建响应式对象

  代码说明:

  1.如果目标对象不是数据或对象,则直接返回对象,并在开发环境中给出错误警告提示。

  2.如果目标已经是代理对象,直接返回目标。(target[__v_raw]的设计非常巧妙:如果目标是代理对象,那么target [_ _ v _ raw]触发get方法,找出目标对象的代理对象是否等于cache对象reactiveMap中的目标本身)。这里处理一个异常。如果对响应对象执行readonly函数,则需要继续执行。

  3.找出reactiveMap中是否有对应的代理对象,然后直接返回对应的代理对象。

  4.确保只有特定的数据可以响应,否则直接返回目标。响应白名单如下:

  1.尚未对目标执行markRaw方法,或者目标对象没有__v_skip属性的值,或者__v_skip属性的值为false。

  2.target不能是不可扩展的对象,即没有对target执行preventExtensions、seal和freeze方法;

  3 .目标是对象或数组;

  4 .目标是地图,集合地图,集合,WeakMap,WeakSet;

  5.通过使用代理函数劫持目标对象,返回的结果是响应对象。根据目标对象的不同,这里的处理函数会有所不同(两个函数都作为参数传入):

  1.1的处理功能。对象或数组是collectionHandlers

  2.map、set、weakmap、weakset的处理函数是baseHandlers

  6.将响应对象存储在reactiveMap中并缓存它。键是目标,值是代理。

  

mutableHandlers 处理函数

  我们知道,访问一个对象的属性会触发get函数,设置一个对象的属性会触发set函数,删除一个对象的属性会触发deleteProperty函数,in操作符会触发has函数,getOwnPropertyNames会触发ownKeys函数。让我们来看看你的函数的代码逻辑。

  

get函数

  由于没有传递参数,isReadonly和shallow是false的默认参数。

  代码逻辑:

  1.如果获取__v_isReactive属性并返回true,则意味着target已经是一个响应对象;

  2.获取__v_isReadonly属性并返回false;(readonly是响应性的另一个API,暂时不解释)

  3.获取__v_raw属性并返回目标本身。此属性用于确定目标是否已经是一个响应对象;

  4.如果目标是数组并且命中了一些属性,比如includes,indexOf,lastIndexOf等。执行数组的这些函数方法,对数组的每个元素进行集合依赖跟踪(arr,trackkotypes.get,I ),然后通过Reflect得到数组函数的值;

  5.反映评价;

  6.判断是否为特殊属性值:symbol,__proto__,__v_isRef,__isVue。如果直接返回到之前获得的res,则不做后续处理;

  7.执行集合依赖项;

  8.如果是ref,如果目标不是数组或者键不是整数,就进行数据解包。这里涉及到另一个响应式APIref,暂且不解释;

  9.如果res是一个对象,递归执行reactive,把res变成一个响应式对象。这里有一个优化提示,只有属性值被访问后才会被劫持,避免了初始化后完全劫持的性能消耗。

  

get函数的的调用时机

  在回答这个问题之前,我们需要回到之前关于setup-揭开Vue3.0 setup函数的神秘面纱的文章。

  在setupStatefulComponent函数中,将执行setup()函数,并得到执行结果:

  处理setupResult结果的逻辑是将区间setupresult赋给instance.setupState:

  这个instance.setupState由instance.ctx表示,所以可以通过访问和修改instance.ctx直接访问和修改instance.setupState:

  我们之前提到过渲染生成子树VNode就是调用render函数。我们用模板编译一下,看看我们例子中的render函数是什么样子的?

  很明显在渲染模板的时候,person属性对象会取自ctx,实际上是setupState的person属性对象。当取setupState的person property对象的姓名、年龄、地址时,将触发get函数的调用,获取相应的值。

  总结:组件实例对象执行render函数生成子树VNode时,会调用响应对象的get函数。

  

track 收集依赖

  收集依赖在上面我们的get函数的代码解释中被提到了两次,那么收集依赖是什么呢?

  要实现响应性,有些功能会在数据发生变化时自动实现,比如执行一些功能。因为副作用渲染函数可以触发组件的重新渲染来更新DOM,所以这里收集的依赖是数据发生变化时需要执行的副作用渲染函数

  即当执行get函数时,将收集相应组件的副作用渲染函数

  我们可以举我们的例子来说明最终的结果:

  

set函数

  代码逻辑:

  1.如果值没有变,直接返回;

  2.通过Reflect设置新值;

  3.它不是原型链上的属性。如果是用于添加属性的add类型的触发器,则是用于修改属性的set类型的触发器。(如果Reflect.set原型链上的属性会再次调用setter,则没有必要执行trigger两次)。

  

trigger 分发依赖

  触发器代码逻辑很明确,就是从get函数收集的依赖targetMap中找到对应的函数,然后执行这些副作用渲染函数来更新DOM。

  

get和副作用渲染函数关联

  我们回过头来回答另一个问题:如何确定从get函数中收集到的副作用渲染函数,即如何确定访问person.name时关联了哪个副作用渲染函数?

  让我们一步一步梳理逻辑:

  组件安装的最后一步mountComponent是执行带副作用的渲染函数:

  SetupRenderEffect首先定义一个componentUpdateFn组件渲染函数,然后将这个componentUpdateFn封装在react veeffect中,将react veeffect对象的run方法赋给component对象的update属性,然后执行update方法,实际上就是在执行react veeffect对象的run方法。

  ReactiveEffect的run方法保存传入的函数。当前场景是componentUpdateFn的组件渲染函数,使用了effectStack和activeEffect两个全局变量。

  执行run方法时,componentUpdateFn被赋给activeEffect,并被推送到效果的堆栈中,然后执行componentUpdateFn方法。执行完成后,componentUpdateFn从栈中释放,activeEffect作为新函数赋在栈顶。

  componentUpdateFn执行时调用renderComponentRoot,本质上是执行组件实例对象的render方法。

  到目前为止,这就是本文的内容。如果在render方法中访问相应的数据,就会触发get函数。get中收集的内容是

  这里设计一个栈结构主要是为了解决嵌套效果的问题。

  

副作用渲染函数的执行过滤

  仔细想想,可能会有疑问?名字,年龄,地址都被修改,然后都关联到同一个渲染函数。理论上,同时修改这三个值会触发三次组件重渲染,这显然是不合理的。那么Vue是如何控制只执行一次的呢?

  我们需要再次回到ReactiveEffect封装componentUpdateFn渲染函数的地方。让我们先来看看第二个参数调度器:

  分发时,如果有调度程序,调度程序将被执行:

  作业的执行逻辑是过滤掉队列中的任务。

  

结尾

  详细介绍了Vue3.0的对应原理:使用代理劫持一个对象,访问对象时会触发get方法,然后进行依赖的收集;当修改对象数据时,会触发set方法,这个方法会是派发依赖,即调用组件的副作用渲染函数(实际上不限于),使组件重新渲染,更新DOM。

  关于vue3.0响应度的文章到此为止。更多相关vue3.0响应,请搜索我们之前的文章或者继续浏览下面的相关文章。希望大家以后能多多支持我们!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: