VUE组件中data属性双向绑定的原理源码部分解读

// src/core/instance/state.js
// 初始化组件的state
export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  // 当组件存在data属性
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  // 当组件存在 computed属性
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

initState会在组件实例化的时候自动触发,该方法主要是完成了初始化data数据,可以看到如果你在组件中定义了data的话  就回去执行initData的方法,那就先看看initData方法

// src/core/instance/state.js
function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  // .....省略无关代码
  
  // 将vue的data传入observe方法
  observe(data, true /* asRootData */)
}

// src/core/observer/index.js
export function observe (value: any, asRootData: ?boolean): Observer | void {
  if (!isObject(value)) {
    return
  }
  let ob: Observer | void
  // ...省略无关代码
  ob = new Observer(value)
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}

在初始化Data的时候实际上失去调用了observe方法,而这个方法就是去实例化了一个Observer对象,去看一下这个对象的类

// src/core/observer/index.js
export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that has this object as root $data

  constructor (value: any) {
    this.value = value
    // 关键代码 new Dep对象
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    // ...省略无关代码
    this.walk(value)
  }

  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      // 给data的所有属性调用defineReactive
      defineReactive(obj, keys[i], obj[keys[i]])
    }
  }
}

在构造函数中调用了walk的方法,该方法就是便利data中的所有属性,并且在遍历的过程中调用了defineReactive方法,defineReactive就是去实现vue双向绑定的基础,本质上就是代理了数据的set,get方法,当数据修改或者获取的时候能够感知,下面看一下defineReactive的具体实现

// src/core/observer/index.js
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 重点,在给具体属性调用该方法时,都会为该属性生成唯一的dep对象
  const dep = new Dep()

  // 获取该属性的描述对象
  // 该方法会返回对象中某个属性的具体描述
 
  const property = Object.getOwnPropertyDescriptor(obj, key)
  // 如果该描述不能被更改,直接返回,因为不能更改,那么就无法代理set和get方法,无法做到响应式
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set

  let childOb = !shallow && observe(val)
  // 重新定义data当中的属性,对get和set进行代理。
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      // 收集依赖, reversedMessage为什么会跟着message变化的原因
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
        }
        if (Array.isArray(value)) {
          dependArray(value)
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      // 通知依赖进行更新
      dep.notify()
    }
  })
}

在代理属性的get方法中,当dep.target存在的时候会调用dep.depend()方法,下面去看一下这个Dep的类

// src/core/observer/dep.js
export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      // 更新 watcher 的值,与 watcher.evaluate() 类似,
      // 但 update 是给依赖变化时使用的,包含对 watch 的处理
      subs[i].update()
    }
  }
}

// 当首次计算 computed 属性的值时,Dep 将会在计算期间对依赖进行收集
Dep.target = null
const targetStack = []

export function pushTarget (_target: Watcher) {
  // 在一次依赖收集期间,如果有其他依赖收集任务开始(比如:当前 computed 计算属性嵌套其他 computed 计算属性),
  // 那么将会把当前 target 暂存到 targetStack,先进行其他 target 的依赖收集,
  if (Dep.target) targetStack.push(Dep.target)
  Dep.target = _target
}

export function popTarget () {
  // 当嵌套的依赖收集任务完成后,将 target 恢复为上一层的 Watcher,并继续做依赖收集
  Dep.target = targetStack.pop()
}

dep.depend就是将该dep对象加入watcher的newDeps()中,

get方法就讲到这里,下面来讲一下set方法,set方法在最后调用了dep.notify(),也就是说在设置vue实例的data属性值的时候,会调用dep.notify(),而notify方法会讲加入到该dep的watch全部更新,也就是说当你在修改data中某个属性值的时候,会同时调用dep.notify()来更新依赖该值的所有watcher

你可能感兴趣的:(Vue.js)