Vue-响应式笔记

207 阅读3分钟

入口文件

当执行new Vue时会进入instance\init.js的_init()初始化方法,执行到initState,再到initData
initData会调用proxy(vm, _data, key)把data的key挂载到Vue实例上面 再调用observe(data, true /* asRootData */)做响应式处理
响应式的处理在 src\core\observer\index.js

开始流程 (只列举部分代码)

响应式

observe(value, asRootData)

  • 判断传入的value是不是对象
  • 是否已经做过响应式处理
  • 没有就为这个对象创建一个响应式属性
function observe(value, asRootData) {
  // 接收一个value对象  asRootData是表示为根数据
  
  // 判断value是否是对象或者是Vnode实例 是就return
  if (!isObject(value) || value instanceof VNode) {
  	// 判断value有没有__ob__ 属性
    if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer)
    // 否则是不是对象或者数组
    else if (Array.isArray(value) || isPlainObject(value)) {
    	// 创建一个Observer对象
        ob = new Observer(value)
    }
  }
  // 返回observer对象
  return ob
}

Observer constructor 为当前对象value创建一个依赖收集 Dep 把当前Observer实例挂载到value的__ob__ 判断value是数组还是对象 分别处理 数组 就把数组的原生方法重新实现,增加数据改变后,发布通知响应式处理 对象 就进行遍历为每个属性进行响应式处理

constructor(value) {
  // 收集依赖
  this.dep = new Dep()
  // 挂载实例
  def(value, '__ob__', this)
  // 判断是不是数组
  if (Array.isArray(value))
  // 数组响应式处理 ...
  // 对象就调用walk遍历每个属性 转换成getter和setter
  this.walk(value)
}

walkObject.keys()每个属性调用defineReactive(obj, keys[i])

defineReactive(obj, keys[i])

  • 为属性创建Dep收集依赖
  • 判断是否需要递归属性的子对象
  • 定义属性的getter和setter
  • getter
    • 判断有没有target,有就收集依赖
    • 再判断有没有子对象,有也为子对象收集依赖
    • 如果子对象是数组就遍历数组元素收集依赖
  • setter
    • 判断值是否发生改变
    • 有改变再判断新值是不是对象
    • 是对象就为这个对象做响应式处理
    • 为当前属性发布通知更新
defineReactive(obj, key) {
  // 创建属性dep
  const dep = new Dep()
  val = obj[key]
  // 判断需不需递归子对象 observe只会处理对象
  // 即使shallow为true 如果val不是对象 会在observe中返回false
  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    get: function() {
      // 如果有target (Watcher)
      if (Dep.target) {
        dep.depend()
        // 有子对象有__ob__ 也收集依赖
        if (childOb) {
          // 当子对象notify时会通知当这个父属性的Watcher
          childOb.dep.depend()
          if (Array.isArray(value)) {
            // 遍历收集
            dependArray(value)
          }
        }
      }
    },
    set: function(newVal) {
      // 如果新值是对象也会进行响应式处理
      childOb = !shallow && observe(newVal)
      // 发布通知
      dep.notify()
    }
  })
}

响应式-收集依赖

src\core\observer\watcher.js是new Watcher创建的文件,$mount执行中会创建一个渲染Watcher,并且在_render生成Vnode时会完成依赖收集
这个过程中Watcher和Dep都会把对方记录下来
在Watcher的记录时,会先判断当前dep是否已经收集过了,避免重复收集 Watcher constructor中会调用get(),再调用pushTarget(this)

function pushTarget (target) {
  // 把当前Watcher实例放到Dep.target
  Dep.target = target
}

生成节点中访问到模板中的属性,触发属性对应的getter

// 因为pushTarget已经把Watcher放到Dep.target
dep.depend()

depend() {
  // 调用Watcher的addDep()
  Dep.target.addDep(this)
}
addDep (dep) {
  const id = dep.id
  // this.newDepIds会记录当前dep是否已经收集了
  // 避免重复收集依赖
  if (!this.newDepIds.has(id)) {
    // Watcher记录了Dep
    this.newDepIds.add(id)
    this.newDeps.push(dep)
    if (!this.depIds.has(id)) {
      // 再调用回dep收集
      dep.addSub(this)
    }
  }
}
// 添加新的订阅者 watcher 对象
addSub (sub) {
  this.subs.push(sub)
}