vue源码解析数据响应式

129 阅读4分钟

initData src\core\instance\state.js

进入到observe src\core\observer\index.js

  • 返回一个observe实例
export function observe (value: any, asRootData: ?boolean): Observer | void {
  // 观察者
  let ob: Observer | void
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {

ob = value.__ob__ 
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {// 创建观察者
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}
  • Observer对象根据数据类型执行对应的响应化操作
export class Observer {
  value: any;
  dep: Dep; 

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    def(value, '__ob__', this) // 在getter中可以通过__ob__可获取ob实例
    if (Array.isArray(value)) { // 数组响应化
      protoAugment(value, arrayMethods)  
      this.observeArray(value)
    } else { // 对象响应化
      this.walk(value)
    }
  }

  /**
   * 遍历对象所有属性定义其响应化
   */
  walk (obj: Object) {
  //获取对象所有的key
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
    //循环遍历执行defineReactive
      defineReactive(obj, keys[i])
    }
  }

  /**
   * 对数组每一项执行observe
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}
  • 对象数据响应式 walk(obj:Object)->definReactive(obj,keys[i) definReactive定义对象属性的getter/setter
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep() // 一个key一个Dep实例

  // 递归执行子对象响应化
  let childOb = !shallow && observe(val)

  // Object.defineProperty数据拦截,定义当前对象getter/setter
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      // getter负责依赖收集
      if (Dep.target) {
      //加入到dep去管理watcher
        dep.depend()
        // 若存在子observer,则依赖也追加到子ob
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value) // 数组需特殊处理
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }    
      val = newVal // 更新值
      //这里递归的目的是,之前的值可能是字符串,赋值可能就是对象,所以childOb更新
      childOb = !shallow && observe(newVal) 
      dep.notify() // 通知更新
    }
  })
}

我们来看看Dep干了什么 src\core\observer\dep.js

export default class Dep {
  static target: ?Watcher; // 依赖收集时的wacher引用
  subs: Array<Watcher>; // watcher数组

  constructor () {
    this.subs = [] 
  }
  //添加watcher实例
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }
  //删除watcher实例
  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }
  //watcher和dep相互保存引用
  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  //通知watcher更新
  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()

    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

其实Dep干了什么事呢?就是管理一组Watcher,Dep关联的值更新时通知其管理的Watcher更新。

我们再来看看Watcher干了什么 src\core\observer\watcher.js

export default class Watcher {
  constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    this.vm = vm
    // 组件保存render watcher,一个组件对应一个渲染watcher
    if (isRenderWatcher) {
      vm._watcher = this
    }
    // 组件保存非render watcher  (wtach,$watch自动创建的watcher)
    vm._watchers.push(this)

    // options...

    // 将表达式解析为getter函数
    // 如果是函数则直接指定为getter,那什么时候是函数?
    // 答案是那些和组件实例对应的Watcher创建时会传递组件更新函数updateComponent
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      // 这种是$watch传递进来的表达式,它们需要解析为函数
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = noop
      }
    }
    // 若非延迟watcher,立即调用getter
    this.value = this.lazy ? undefined : this.get()
  }

  /**
   * 模拟getter, 重新收集依赖re-collect dependencies.
   */
  get () {
    // Dep.target = this
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      // 从组件中获取到value同时触发依赖收集
      value = this.getter.call(vm, vm)
    } 
    catch (e) {} 
    finally {
      // deep watching,递归触发深层属性
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }

    addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      // watcher保存dep引用
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      // dep添加watcher
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
    }
  }

  update () {
    // 更新逻辑
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      //默认lazy和sync都是false,所以会走该逻辑,异步更新
      queueWatcher(this)
    }
  }
}

其实Watcher干了什么事呢?监控一个表达式或关联一个组件更新函数,数值更新则指定回调或更新函数(异步更新)被调用。

watcher的数据异步更新queueWatcher怎么实现的。

export function queueWatcher (watcher: Watcher) {
  const id = watcher.id
  if (has[id] == null) {
    has[id] = true
    if (!flushing) {
      // 入队
      queue.push(watcher)
    } else {
      let i = queue.length - 1
      while (i > index && queue[i].id > watcher.id) {
        i--
      }
      queue.splice(i + 1, 0, watcher)
    }
    // queue the flush
    if (!waiting) {
      waiting = true

      // 执行异步操作
      nextTick(flushSchedulerQueue)
    }
  }
}

nextTick执行异步操作

export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  // callbacks排队做操作
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    // 异步执行策略,兼容性处理
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }

其实数据在做更新时,不会立刻更新,是异步更新。入队操作,入队批量一起执行。

  • 数组响应式

arrayMethods数组方法打补丁 vue\src\core\observer\array.js

// 数组原型
const arrayProto = Array.prototype
// 修改后的原型
export const arrayMethods = Object.create(arrayProto)
// 七个待修改方法
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * 拦截这些方法,额外发送变更通知
 */
methodsToPatch.forEach(function (method) {
  // 原始数组方法
  const original = arrayProto[method]
  // 修改这些方法的descriptor
  def(arrayMethods, method, function mutator (...args) {
    // 原始操作
    const result = original.apply(this, args)
    // 获取ob实例用于通知更新
    const ob = this.__ob__
    // 三个能新增元素的方法特殊处理
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // 若有新增则做响应处理
    if (inserted) ob.observeArray(inserted)
    // 通知更新
    ob.dep.notify()
    return result
  })
})

protoAugment(value,arrayMethods)覆盖数组原型 vue\src\core\observer\index.js

function protoAugment (target, src: Object) {
  // 覆盖原来的原型,按照我们拦截后的方法执行的
  target.__proto__ = src
}

数组响应式的特殊处理observeArray() vue\src\core\observer\index.js

observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }

依赖收集时特殊处理 vue\src\core\observer\index.js

//getter中
if (Array.isArray(value)) {
  dependArray(value)
}

// 数组中每一项也需要收集依赖
function dependArray (value: Array<any>) {
  for (let e, i = 0, l = value.length; i < l; i++) {
    e = value[i]
    e && e.__ob__ && e.__ob__.dep.depend()
    if (Array.isArray(e)) {
      dependArray(e)
    }
  }
}