美文网首页
Vue 响应式

Vue 响应式

作者: _1633_ | 来源:发表于2020-12-08 23:06 被阅读0次

initState

initState

     initState ⽅法主要是对 data、 props 、 methods 、 computed 和 wathcer 等属性做了初 始化操作。

initState

    比如 data , data 的初始化主要过程也是做两件事,⼀个是对定义 data 函数返回对象的遍历,通过 proxy 把每⼀个值 vm._data.xxx 都代理到 vm.xxx 上;另⼀个是调用  observe 方法观测整个 data 的变化,把 data 也变成响应式,可以通过 vm._data.xxx 访问到定义 data 返回函数中对应的 属性。 

    proxy 就是数据代理, 把 vm._data.xxx 代理都  vm.xxx上。

initData

observe

     observe 就是给 非VNode 的对象类型数据添加⼀个 Observer ,如果已经添加过则直接 返回,否则在满足⼀定条件下去实例化⼀个 Observer 对象实例。

observe

     Observer 是⼀个类,它的作用是给对象的属性添加 getter 和 setter,用于依赖收集和派发更新

            首先实例化 Dep 对象(这里的dep 是保存数组类型数据的依赖,对象的会在当前的defineReactive创建),接着通过执行 def 函数把 自身实例 添加到数据对象 value 的 __ob__ 属性上,数组会调⽤ observeArray 方法, 纯对象调用  walk 方法。

            observeArray 是遍历数组再次调⽤ observe 方法; walk ⽅法是遍历对象的 key 调方  defineReactive$$1  方法

Observer def

    defineReactive$$1  函数最开始初始化 Dep 对象的实例,接着拿到 obj 的属性描述符,然后对子对象 递归调用  observe 方法,这样就保证了无论  obj 的结构多复杂,它的所有子属性也能变成响应式的对象,这样我们访问或修改 obj 中⼀个嵌套较深的属性,也能触发 getter 和 setter。最后利用 Object.defineProperty 去给 obj 的属性 key 添加 getter 和 setter。

defineReactive$$1

依赖收集

    上面代码中多次出现了 Dep ,Dep 是整个 getter 依赖收集的核心,它有⼀个静态属性 target , 这是⼀个 全局唯一  Watcher ,因为在同⼀时间只能有⼀个全局的 Watcher 被计算,另外它的属性 subs 一个元素为 Watcher 的数组。

Dep

    结合 watcher 讲讲 Dep

Watcher

     Vue 的 mount 中的  mountComponent 函数

mountComponent

    实例化了一个 渲染 Watcher, Watcher 执行了 this.get() 方法; 

    首先 pushTarget(), Dep.target = 当前的渲染 Watcher 

     再执行 this.getter , 也就是 updateComponent 方法:首先在 _render() 渲染 Vnode 过程中会访问 vm 的data,就触发了每个数据 getter 方法;

     每个数据 都有一个 dep;

 每个数据 都有一个 dep

        在触发 getter 的时候会调用  dep.depend() 方法,也就会执行  Dep.target.addDep(this) ;

         Dep.target 已经为 渲染 Watcher, 也就是执行 watcher.addDep(dep);

         注意: 在 watcher.addDep 中,this.newDeps.push(dep), watcher 保存了dep 的引用;dep.addSub(this) dep 也添加了watcher ,这是一个双向保存的过程,两者都保存了对方。

addDep

        dep.addSub(this) 也就是执行 this.subs.push(sub),在当前 data 中的这个数据 的 dep.subs 中 放入 当前的 Watcher,这个⽬的是为后续数据变化时候能通知到哪些 subs 做准备。

        所以在 vm._render() 过程中,会触发所有数据的 getter,这样实际上已经完成了⼀个依赖收集的过程。

        在执行完 Watcher 的 get 方法后 if (this.deep) { traverse(value) } ,会递归访问 value,触发它所有子元素 的 getter;

        接着 popTarget(),Dep.target = targetStack.pop() ,把 Dep.target 恢复成上⼀个状态,因为当前 vm 的数据依赖收集已经完成,那么对应的 渲染 Dep.target 也需要改变。

        收集依赖的目的是为了当这些响应式数据发送变化,触发它们的 setter 的时候,能知道应该通知哪 些订阅者去做相应的逻辑处理


    派发更新

        当我们重新设置 data 中的数据的时候,就会 触发 setter, setter 就会 notify 该数据的 dep.subs 收集的 watcher。

          for (var i = 0, l = subs.length; i < l; i++) {

            subs[i].update();

          }

        遍历所有的 subs,就是 Watcher 的 实例数组,然后调用每个 watcher 的 update 方法;

update

    首先它会用 has 判断 保证同一个 watcher 只能添加一次;通过 wating 保证对 nextTick(flushSchedulerQueue) 的调用逻辑只有⼀次;

    这里有一个队列的概念,是 Vue 在做派发更新的时候的一个优化点,它并不会每次数据修改都会触发 watcher 的回调,而是把这些 watcher 先添加到一个队列里,然后在 nextTick 后执行flushSchedulerQueue。

    首先会进行 队列排序, 接着进行遍历, 拿到对应的 watcher 执行 run()。

    在遍历的时候 每次都对 queue.length 求值,因为在 watcher.run() 的时候,很可能会添加新的 watcher,这样会再次执行 queueWatcher,而此时的 flushing 为 true, 就会走 else 逻辑,,然后就会从后往前找,找到第⼀个待 插⼊ watcher 的 id 比当前队列中 watcher 的 id 大的位置。把 watcher 按照 id 的插⼊到队列 中,因此 queue 的长度发送了变化。

flushSchedulerQueue

    先通过 this.get() 得到它当前的值,然后做判断,如果 满足新旧值不等、新值是对象类型、 deep 模式任何⼀个条件,则执行  watcher 的回调,注意回调 函数执行的时候会把第一个和第⼆个参数传入新值 value 和旧值 oldValue ,这就是当我们添加自定义 user watcher 的时候能在回调函数的参数中拿到新旧值的原因

    那么对于渲染 watcher 而言,它在执行  this.get() 方法求值的时候,会执行  getter ⽅法:

    updateComponent = () => { vm._update(vm._render(), hydrating) }

    所以这就是当我们去修改组件相关的响应式数据的时候,会触发组件重新渲染的原因,接着就会重新执行  patch 的过程,后面的过程 大部分和首次渲染相同,有一点小区别。

run

    执行完 run, 往后执行 resetSchedulerState 就是 恢复状态,把一些变量恢复初始值,把 watcher 队列清空。

resetSchedulerState

总结

     Vue 数据修改派发更新实际上就是当数据发生变化的 时候,触发 setter 逻辑,把在依赖过程中订阅的的所有观察者,也就是 watcher ,都触发它们的 update 过程,这个过程⼜利用了队列做了进⼀步优化,在 nextTick 后执行所有 watcher.run() ,最后执行它们的回调函数。

       setter---> dep.notify() ---> subs[i].update() ---> queueWatcher ---> nextTick(flushSchedulerQueue)

相关文章

网友评论

      本文标题:Vue 响应式

      本文链接:https://www.haomeiwen.com/subject/dcdqsktx.html