天天看點

Vue的資料依賴實作原理簡析

首先讓我們從最簡單的一個執行個體​

​Vue​

​入手:

const app = new Vue({
        // options  傳入一個選項obj.這個obj即對于這個vue執行個體的初始化
    })
      

通過查閱文檔,我們可以知道這個​

​options​

​可以接受:

  • 選項/資料
  • data
  • props
  • propsData(友善測試使用)
  • computed
  • methods
  • watch
  • 選項 / DOM
  • 選項 / 生命周期鈎子
  • 選項 / 資源
  • 選項 / 雜項

具體未展開的内容請自行查閱相關文檔,接下來讓我們來看看傳入的​

​選項/資料​

​是如何管理資料之間的互相依賴的。

const app = new Vue({
        el: '#app',
        props: {
          a: {
            type: Object,
            default () {
              return {
                key1: 'a',
                key2: {
                    a: 'b'
                }
              }
            }
          }
        },
        data: {
          msg1: 'Hello world!',
          arr: {
            arr1: 1
          }
        },
        watch: {
          a (newVal, oldVal) {
            console.log(newVal, oldVal)
          }
        },
        methods: {
          go () {
            console.log('This is simple demo')
          }
        }
    })
      

我們使用​

​Vue​

​這個構造函數去執行個體化了一個​

​vue​

​執行個體​

​app​

​。傳入了​

​props​

​, ​

​data​

​watch​

​methods​

​等屬性。在執行個體化的過程中,​

​Vue​

​提供的構造函數就使用我們傳入的​

​options​

​去完成資料的依賴管理,初始化的過程隻有一次,但是在你自己的程式當中,資料的依賴管理的次數不止一次。

那​

​Vue​

​的構造函數到底是怎麼實作的呢?​​Vue​​

// 構造函數
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

// 對Vue這個class進行mixin,即在原型上添加方法
// Vue.prototype.* = function () {}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
      

當我們調用​

​new Vue​

​的時候,事實上就調用的​

​Vue​

​原型上的​

​_init​

​方法.

// 原型上提供_init方法,建立一個vue執行個體并傳入options參數
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    let startTag, endTag
    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      // 将傳入的這些options選項挂載到vm.$options屬性上
      vm.$options = mergeOptions(
        // components/filter/directive
        resolveConstructorOptions(vm.constructor),
        // this._init()傳入的options
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm     // 自身的執行個體
    // 接下來所有的操作都是在這個執行個體上添加方法
    initLifecycle(vm)  // lifecycle初始化
    initEvents(vm)     // events初始化 vm._events, 主要是提供vm執行個體上的$on/$emit/$off/$off等方法
    initRender(vm)     // 初始化渲染函數,在vm上綁定$createElement方法
    callHook(vm, 'beforeCreate')  // 鈎子函數的執行, beforeCreate
    initInjections(vm) // resolve injections before data/props
    initState(vm)      // Observe data添加對data的監聽, 将data轉化為getters/setters
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created') // 鈎子函數的執行, created

    // vm挂載的根元素
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
      

其中在​

​this._init()​

​方法中調用​

​initState(vm)​

​,完成對​

​vm​

​這個執行個體的資料的監聽,也是本文所要展開說的具體内容。

export function initState (vm: Component) {
  // 首先在vm上初始化一個_watchers數組,緩存這個vm上的所有watcher
  vm._watchers = []
  // 擷取options,包括在new Vue傳入的,同時還包括了Vue所繼承的options
  const opts = vm.$options
  // 初始化props屬性
  if (opts.props) initProps(vm, opts.props)
  // 初始化methods屬性
  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)
  // 初始化watch屬性
  if (opts.watch) initWatch(vm, opts.watch)
}
      

initProps

我們在執行個體化​

​app​

​的時候,在構造函數裡面傳入的​

​options​

​中有​

​props​

​屬性:

props: {
      a: {
        type: Object,
        default () {
          return {
            key1: 'a',
            key2: {
                a: 'b'
            }
          }
        }
      }
    }
      
function initProps (vm: Component, propsOptions: Object) {
  // propsData主要是為了友善測試使用
  const propsData = vm.$options.propsData || {}
  // 建立vm._props對象,可以通過app執行個體去通路
  const props = vm._props = {}
  // cache prop keys so that future props updates can iterate using Array
  // instead of dynamic object key enumeration.
  // 緩存的prop key
  const keys = vm.$options._propKeys = []
  const isRoot = !vm.$parent
  // root instance props should be converted
  observerState.shouldConvert = isRoot
  for (const key in propsOptions) {
    // this._init傳入的options中的props屬性
    keys.push(key)
    // 注意這個validateProp方法,不僅完成了prop屬性類型驗證的,同時将prop的值都轉化為了getter/setter,并傳回一個observer
    const value = validateProp(key, propsOptions, propsData, vm)
   
    // 将這個key對應的值轉化為getter/setter
      defineReactive(props, key, value)
    // static props are already proxied on the component's prototype
    // during Vue.extend(). We only need to proxy props defined at
    // instantiation here.
    // 如果在vm這個執行個體上沒有key屬性,那麼就通過proxy轉化為proxyGetter/proxySetter, 并挂載到vm執行個體上,可以通過app._props[key]這種形式去通路
    if (!(key in vm)) {
      proxy(vm, `_props`, key)
    }
  }
  observerState.shouldConvert = true
}
      

接下來看下​

​validateProp(key, propsOptions, propsData, vm)​

​方法内部到底發生了什麼。

export function validateProp (
  key: string,
  propOptions: Object,    // $options.props屬性
  propsData: Object,      // $options.propsData屬性
  vm?: Component
): any {
  const prop = propOptions[key]
  // 如果在propsData測試props上沒有緩存的key
  const absent = !hasOwn(propsData, key)
  let value = propsData[key]
  // 處理boolean類型的資料
  // handle boolean props
  if (isType(Boolean, prop.type)) {
    if (absent && !hasOwn(prop, 'default')) {
      value = false
    } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) {
      value = true
    }
  }
  // check default value
  if (value === undefined) {
    // default屬性值,是基本類型還是function
    // getPropsDefaultValue見下面第一段代碼
    value = getPropDefaultValue(vm, prop, key)
    // since the default value is a fresh copy,
    // make sure to observe it.
    const prevShouldConvert = observerState.shouldConvert
    observerState.shouldConvert = true
    // 将value的所有屬性轉化為getter/setter形式
    // 并添加value的依賴
    // observe方法的分析見下面第二段代碼
    observe(value)
    observerState.shouldConvert = prevShouldConvert
  }
  if (process.env.NODE_ENV !== 'production') {
    assertProp(prop, key, value, vm, absent)
  }
  return value
}
      
// 擷取prop的預設值
function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): any {
  // no default, return undefined
  // 如果沒有default屬性的話,那麼就傳回undefined
  if (!hasOwn(prop, 'default')) {
    return undefined
  }
  const def = prop.default
  // the raw prop value was also undefined from previous render,
  // return previous default value to avoid unnecessary watcher trigger
  if (vm && vm.$options.propsData &&
    vm.$options.propsData[key] === undefined &&
    vm._props[key] !== undefined) {
    return vm._props[key]
  }
  // call factory function for non-Function types
  // a value is Function if its prototype is function even across different execution context
  // 如果是function 則調用def.call(vm)
  // 否則就傳回default屬性對應的值
  return typeof def === 'function' && getType(prop.type) !== 'Function'
    ? def.call(vm)
    : def
}
      

​Vue​

​提供了一個​

​observe​

​方法,在其内部執行個體化了一個​

​Observer​

​類,并傳回​

​Observer​

​的執行個體。每一個​

​Observer​

​執行個體對應記錄了​

​props​

​中這個的​

​default value​

​的所有依賴(僅限​

​object​

​類型),這個​

​Observer​

​實際上就是一個觀察者,它維護了一個數組​

​this.subs = []​

​用以收集相關的​

​subs(訂閱者)​

​(即這個觀察者的依賴)。通過将​

​default value​

​轉化為​

​getter/setter​

​形式,同時添加一個自定義​

​__ob__​

​屬性,這個屬性就對應​

​Observer​

​執行個體。

說起來有點繞,還是讓我們看看我們給的​

​demo​

​裡傳入的​

​options​

​配置:

props: {
      a: {
        type: Object,
        default () {
          return {
            key1: 'a',
            key2: {
                a: 'b'
            }
          }
        }
      }
    }
      

在往上數的第二段代碼裡面的方法​

​obervse(value)​

​,即對​

​{key1: 'a', key2: {a: 'b'}}​

​進行依賴的管理,同時将這個​

​obj​

​所有的屬性值都轉化為​

​getter/setter​

​形式。此外,​

​Vue​

​還會将​

​props​

​屬性都代理到​

​vm​

​執行個體上,通過​

​vm.key1​

​,​

​vm.key2​

​就可以通路到這個屬性。

此外,還需要了解下在​

​Vue​

​中管理依賴的一個非常重要的類: ​

​Dep​

export default class Dep { 
  constructor () {
    this.id = uid++
    this.subs = []
  }
  addSub () {...}  // 添加訂閱者(依賴)
  removeSub () {...}  // 删除訂閱者(依賴)
  depend () {...}  // 檢查目前Dep.target是否存在以及判斷這個watcher已經被添加到了相應的依賴當中,如果沒有則添加訂閱者(依賴),如果已經被添加了那麼就不做處理
  notify () {...}  // 通知訂閱者(依賴)更新
}
      

在​

​Vue​

​的整個生命周期當中,你所定義的響應式的資料上都會綁定一個​

​Dep​

​執行個體去管理其依賴。它實際上就是​

​觀察者​

​和​

​訂閱者​

​聯系的一個橋梁。

剛才談到了對于依賴的管理,它的核心之一就是觀察者​

​Observer​

​這個類:

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
    // dep記錄了和這個value值的相關依賴
    this.dep = new Dep()
    this.vmCount = 0
    // value其實就是vm._data, 即在vm._data上添加__ob__屬性
    def(value, '__ob__', this)
    // 如果是數組
    if (Array.isArray(value)) {
      // 首先判斷是否能使用__proto__屬性
      const augment = hasProto
        ? protoAugment
        : copyAugment
      augment(value, arrayMethods, arrayKeys)
      // 周遊數組,并将obj類型的屬性改為getter/setter實作
      this.observeArray(value)
    } else {
      // 周遊obj上的屬性,将每個屬性改為getter/setter實作
      this.walk(value)
    }
  }

  /**
   * Walk through each property and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  // 将每個property對應的屬性都轉化為getter/setters,隻能是當這個value的類型為Object時
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i], obj[keys[i]])
    }
  }

  /**
   * Observe a list of Array items.
   */
  // 監聽array中的item
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}
      

​walk​

​方法裡面調用​

​defineReactive​

​方法:通過周遊這個​

​object​

​的​

​key​

​,并将對應的​

​value​

​getter/setter​

​形式,通過閉包維護一個​

​dep​

​,在​

​getter​

​方法當中定義了這個​

​key​

​是如何進行依賴的收集,在​

​setter​

​方法中定義了當這個​

​key​

​對應的值改變後,如何完成相關依賴資料的更新。但是從源碼當中,我們卻發現當​

​getter​

​函數被調用的時候并非就一定會完成依賴的收集,其中還有一層判斷,就是​

​Dep.target​

​是否存在。

/**
 * Define a reactive property on an Object.
 */
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: Function
) {
  // 每個屬性建立一個dep執行個體,管理這個屬性的依賴
  const dep = new Dep()
    
  // 或者屬性描述符
  const property = Object.getOwnPropertyDescriptor(obj, key)
  // 如果這個屬性是不可配的,即無法更改
  if (property && property.configurable === false) {
    return
  }

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

  // 遞歸去将val轉化為getter/setter
  // childOb将子屬性也轉化為Observer
  let childOb = observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    // 定義getter -->> reactiveGetter
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      // 定義相應的依賴
      if (Dep.target) {
        // Dep.target.addDep(this)
        // 即添加watch函數
        // dep.depend()及調用了dep.addSub()隻不過中間需要判斷是否這個id的dep已經被包含在内了
        dep.depend()
        // childOb也添加依賴
        if (childOb) {
          childOb.dep.depend()
        }
        if (Array.isArray(value)) {
          dependArray(value)
        }
      }
      return value
    },
    // 定義setter -->> reactiveSetter
    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
      }
      // 對得到的新值進行observe
      childOb = observe(newVal)
      // 相應的依賴進行更新
      dep.notify()
    }
  })
}
      

在上文中提到了​

​Dep​

​類是連結​

​觀察者​

​訂閱者​

​的橋梁。同時在​

​Dep​

​的實作當中還有一個非常重要的屬性就是​

​Dep.target​

​,它事實就上就是一個訂閱者,隻有當​

​Dep.target​

​(訂閱者)存在的時候,調用屬性的​

​getter​

​函數的時候才能完成依賴的收集工作。

Dep.target = null
const targetStack = []

export function pushTarget (_target: Watcher) {
  if (Dep.target) targetStack.push(Dep.target)
  Dep.target = _target
}

export function popTarget () {
  Dep.target = targetStack.pop()
}
      

那麼​

​Vue​

​是如何來實作​

​訂閱者​

​的呢?​

​Vue​

​裡面定義了一個類: ​

​Watcher​

​Vue​

​的整個生命周期當中,會有4類地方會執行個體化​

​Watcher​

​:

  • Vue

    執行個體化的過程中有

    watch

    選項
  • Vue

    computed

    計算屬性選項
  • Vue

    原型上有挂載

    $watch

    方法: Vue.prototype.$watch,可以直接通過執行個體調用

    this.$watch

    方法
  • Vue

    生成了

    render

    函數,更新視圖時
constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: Object
  ) {
    // 緩存這個執行個體vm
    this.vm = vm
    // vm執行個體中的_watchers中添加這個watcher
    vm._watchers.push(this)
    // options
    if (options) {
      this.deep = !!options.deep
      this.user = !!options.user
      this.lazy = !!options.lazy
      this.sync = !!options.sync
    } else {
      this.deep = this.user = this.lazy = this.sync = false
    }
    this.cb = cb
    this.id = ++uid // uid for batching
    this.active = true
    this.dirty = this.lazy // for lazy watchers
    ....
    // parse expression for getter
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = function () {}
      }
    }
    // 通過get方法去擷取最新的值
    // 如果lazy為true, 初始化的時候為undefined
    this.value = this.lazy
      ? undefined
      : this.get()
  }
  get () {...}
  addDep () {...}
  update () {...}
  run () {...}
  evaluate () {...}
  run () {...}
      

​Watcher​

​接收的參數當中​

​expOrFn​

​定義了用以擷取​

​watcher​

​getter​

​函數。​

​expOrFn​

​可以有2種類型:​

​string​

​或​

​function​

​.若為​

​string​

​類型,首先會通過​

​parsePath​

​方法去對​

​string​

​進行分割(僅支援​

​.​

​号形式的對象通路)。在除了​

​computed​

​選項外,其他幾種執行個體化​

​watcher​

​的方式都是在執行個體化過程中完成求值及依賴的收集工作:​

​this.value = this.lazy ? undefined : this.get()​

​.在​

​Watcher​

​get​

​方法中:

!!!前方高能

get () {
 // pushTarget即設定目前的需要被執行的watcher
    pushTarget(this)
    let value
    const vm = this.vm
    if (this.user) {
      try {
        // $watch(function () {})
        // 調用this.getter的時候,觸發了屬性的getter函數
        // 在getter中進行了依賴的管理
        value = this.getter.call(vm, vm)
        console.log(value)
      } catch (e) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      }
    } else {
      // 如果是建立模闆函數,則會動态計算模闆與data中綁定的變量,這個時候就調用了getter函數,那麼就完成了dep的收集
      // 調用getter函數,則同時會調用函數内部的getter的函數,進行dep收集工作
      value = this.getter.call(vm, vm)
    }
    // "touch" every property so they are all tracked as
    // dependencies for deep watching
    // 讓每個屬性都被作為dependencies而tracked, 這樣是為了deep watching
    if (this.deep) {
      traverse(value)
    }
    popTarget()
    this.cleanupDeps()
    return value    
}
      

一進入​

​get​

​方法,首先進行​

​pushTarget(this)​

​的操作,此時​

​Vue​

​當中​

​Dep.target = 目前這個watcher​

​,接下來進行​

​value = this.getter.call(vm, vm)​

​操作,在這個操作中就完成了依賴的收集工作。還是拿文章一開始的​

​demo​

​來說,在​

​vue​

​執行個體化的時候傳入了​

​watch​

​選項:

props: {
      a: {
        type: Object,
        default () {
          return {
            key1: 'a',
            key2: {
                a: 'b'
            }
          }
        }
      }
    },
   watch: {
        a (newVal, oldVal) {
            console.log(newVal, oldVal)
        }
    }, 
      

​Vue​

​initState()​

​開始執行後,首先會初始化​

​props​

​的屬性為​

​getter/setter​

​函數,然後在進行​

​initWatch​

​初始化的時候,這個時候初始化​

​watcher​

​執行個體,并調用​

​get()​

​方法,設定​

​Dep.target = 目前這個watcher執行個體​

​,進而到​

​value = this.getter.call(vm, vm)​

​的操作。在調用​

​this.getter.call(vm, vm)​

​的方法中,便會通路​

​props​

​選項中的​

​a​

​屬性即其​

​getter​

​函數。在​

​a​

​屬性的​

​getter​

​函數執行過程中,因為​

​Dep.target​

​已經存在,那麼就進入了​

​依賴收集​

​的過程:

if (Dep.target) {
    // Dep.target.addDep(this)
    // 即添加watch函數
    // dep.depend()及調用了dep.addSub()隻不過中間需要判斷是否這個id的dep已經被包含在内了
    dep.depend()
    // childOb也添加依賴
    if (childOb) {
      childOb.dep.depend()
    }
    if (Array.isArray(value)) {
      dependArray(value)
    }
  }
      

​dep​

​是一開始初始化的過程中,這個屬性上的​

​dep​

​屬性。調用​

​dep.depend()​

​函數:

depend () {
    if (Dep.target) {
      // Dep.target為一個watcher
      Dep.target.addDep(this)
    }
  }
      

​Dep.target​

​也就剛才的那個​

​watcher​

​執行個體,這裡也就相當于調用了​

​watcher​

​執行個體的​

​addDep​

​方法: ​

​watcher.addDep(this)​

​,并将​

​dep​

​觀察者傳入。在​

​addDep​

​方法中完成依賴收集:

addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
    }
  }
      

這個時候依賴完成了收集,當你去修改​

​a​

​屬性的值時,會調用​

​a​

​setter​

​函數,裡面會執行​

​dep.notify()​

​,它會周遊所有的訂閱者,然後調用訂閱者上的​

​update​

​函數。

​initData​

​過程和​

​initProps​

​類似,具體可參見源碼。

initComputed

以上就是在​

​initProps​

​過程中​

​Vue​

​是如何進行依賴收集的,​

​initData​

​的過程和​

​initProps​

​類似,下來再來看看​

​initComputed​

​的過程.

​computed​

​屬性初始化的過程當中,會為每個屬性執行個體化一個​

​watcher​

​:

const computedWatcherOptions = { lazy: true }

function initComputed (vm: Component, computed: Object) {
  // 建立_computedWatchers屬性
  const watchers = vm._computedWatchers = Object.create(null)

  for (const key in computed) {
    const userDef = computed[key]
    // 如果computed為funtion,即取這個function為getter函數
    // 如果computed為非function.則可以單獨為這個屬性定義getter/setter屬性
    let getter = typeof userDef === 'function' ? userDef : userDef.get
    // create internal watcher for the computed property.
    // lazy屬性為true
    // 注意這個地方傳入的getter參數
    // 執行個體化的過程當中不去完成依賴的收集工作
    watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)

    // component-defined computed properties are already defined on the
    // component prototype. We only need to define computed properties defined
    // at instantiation here.
    if (!(key in vm)) {
      defineComputed(vm, key, userDef)
    } 
  }
}
      

但是這個​

​watcher​

​在執行個體化的過程中,由于傳入了​

​{lazy: true}​

​的配置選項,那麼一開始是不會進行求值與依賴收集的: ​

​this.value = this.lazy ? undefined : this.get()​

​initComputed​

​的過程中,​

​Vue​

​會将​

​computed​

​屬性定義到​

​vm​

​執行個體上,同時将這個屬性定義為​

​getter/setter​

​。當你通路​

​computed​

​屬性的時候調用​

​getter​

function createComputedGetter (key) {
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      // 是否需要重新計算
      if (watcher.dirty) {
        watcher.evaluate()
      }
      // 管理依賴
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
}
      

​watcher​

​存在的情況下,首先判斷​

​watcher.dirty​

​屬性,這個屬性主要是用于判斷這個​

​computed​

​屬性是否需要重新求值,因為在上一輪的依賴收集的過程當中,觀察者已經将這個​

​watcher​

​添加到依賴數組當中了,如果觀察者發生了變化,就會​

​dep.notify()​

​,通知所有的​

​watcher​

​,而對于​

​computed​

​watcher​

​接收到變化的請求後,會将​

​watcher.dirty = true​

​即表明觀察者發生了變化,當再次調用​

​computed​

​getter​

​函數的時候便會重新計算,否則還是使用之前緩存的值。

initWatch

​initWatch​

​的過程中其實就是執行個體化​

​new Watcher​

​完成觀察者的依賴收集的過程,在内部的實作當中是調用了原型上的​

​Vue.prototype.$watch​

​方法。這個方法也适用于​

​vm​

​執行個體,即在​

​vm​

​執行個體内部調用​

​this.$watch​

​方法去執行個體化​

​watcher​

​,完成依賴的收集,同時監聽​

​expOrFn​

​的變化。

總結:

​Vue​

​執行個體初始化的過程中實作依賴管理的分析。大緻的總結下就是:

  • initState

    的過程中,将

    props

    ,

    computed

    data

    等屬性通過

    Object.defineProperty

    來改造其

    getter/setter

    屬性,并為每一個響應式屬性執行個體化一個

    observer

    觀察者。這個

    observer

    内部

    dep

    記錄了這個響應式屬性的所有依賴。
  • 當響應式屬性調用

    setter

    函數時,通過

    dep.notify()

    方法去周遊所有的依賴,調用

    watcher.update()

    去完成資料的動态響應。

這篇文章主要從初始化的資料層面上分析了​

​Vue​

​是如何管理依賴來到達資料的動态響應。下一篇文章來分析下​

​Vue​

​中模闆中的指令和響應式資料是如何關聯來實作由資料驅動視圖,以及資料是如何響應視圖變化的。