vue响应式原理

响应式原理:组件再初始化的时候,会执行initState这个函数,然后再执行initData这个函数,然后执行observe,在执行new Observer(value)
然后使用def函数,给当前遍历的data添加__ob__,不直接添加是想让这个属性不可枚举
然后针对数组和对象做不同的处理,对象是遍历每个属性,把他变成响应式,数组则是遍历每一个子项,如果子项是对象,再遍历子项对象里的键值对,
所以我们可知,数组的子项并不是响应式的数据,但是子项内部如果是对象等,会是响应式

依赖收集:由于初始化的时候,数据变成了响应式,然后我们再渲染的时候,执行mountComponent,定义updateComponent,然后把他传入watcher,然后执行。执行updateComponent就是执行vm._update(vm._render(), hydrating),先执行vm._render(),即执行render.call(vm._renderProxy, vm.$createElement),由于render函数中,会访问到我们再data定义的变量,所以会触发get

Dep.target的逻辑我们可以看到最开始我们的逻辑,执行mountComponent,然后执行new Watcher,然后执行pushTarget(this),赋值Dep.target,然后再执行this.getter.call(vm, vm),即updateComponent,所以再此处,Dep.target就是渲染watcher,所以继续执行dep.depend

由于Dep.target就是渲染watcher,所以继续执行Watcher上的方法addDep,并将this传入,this这里就是dep,然后执行

newDepIds,其实就是每次重新渲染对应的dep的ld集合,初始化的时候为空,所以执行dep.addSub(this),这里的this就是渲染watcher

addSub就比较简单了,就是将watcher放入subs内,至此,依赖结束

我们回到最开始的逻辑,执行mountComponent,然后执行new Watcher,然后执行pushTarget(this),赋值Dep.target,然后再执行this.getter.call(vm, vm),即updateComponent,后续逻辑就是我们上面所述的依赖收集,收集完了之后,执行

popTarget();
this.cleanupDeps();

 先说popTarget,为啥要用栈的形式来存Watcher,我的理解是,由于渲染时一个深度优先的过程。比如你渲染父组件,Dep.target是父的渲染Watcher,然后中途解析到多个子组件,你需要把子组件入栈再出栈,并且出栈后,还需要标识是父组件在渲染,即Dep.target为父,我理解如果是多个子组件需要这样

然后执行cleanupDeps,这一步,清空了newDeps,和newDepIds还有移走一些subs中的watcher,因为在切换过程中,有一些内容,原本订阅了这个watcher,但是由于切换之后,不显示了,所以render里将访问不到,即新的newDepIds没有对应的dep了,在这一步就会对比新的newDepIds,如果之前的内容有些不在新的里面,就会移除他

举一个例子,一个div上的v-if由true变成false,先触发set,然后dep.notify,然后执行一系列函数后到了Watcher.run,然后执行this.get(),然后执行this.getter.call(vm, vm),即updateComponent,然后又会去执行render生成Vnode,由于此次,v-if为false,将不会再去访问这个div上的变量,所以newDepIds没有这个了,所以执行完之后回到Watcher.get会行 this.cleanupDeps()移除subs里的watcher,下次这个变量在改变,subs就为空了,没订阅者,所以不在执行后续逻辑

派发更新:由于数据是响应式的,所以当你改变变量的时候,会触发set,然后走到childOb = !shallow && observe(newVal)给新赋值的变量也变成响应式,然后执行dep.notify()更新视图

dep.notify会遍历subs,然后执行update,其实就是通知每一个订阅者,即每一个watcher,去更新对应的试图

执行update,会先执行queueWatcher,把watcher往一个队列里面推,主要是防止多个相同的watcher更新,比如a和b二个变量,都被一个watcher订阅,他们同时改变,只需要更新一次即可。

然后会nextTick(flushSchedulerQueue),即在异步任务里执行更新操作,因为一个同步任务里,可能会多次更新,所以把更新放在异步里面,并且做了一个去重优化

flushSchedulerQueue函数会先进行排序,主要是为了让userWatcher先更新,即我们在组件内定义的watcher,并且保证更新顺序是由父到子。然后就遍历queue,这里没缓存queue的长度是因为可能在更新过程中,会往queue里继续推watcher。然后就执行watcher.run更新视图,然后执行this.get(),然后执行mountComponent重新收集依赖,更新

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值