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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。