亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Vue 2.0的數(shù)據(jù)依賴實現(xiàn)原理代碼簡析

 更新時間:2017年07月10日 10:07:06   作者:蘋果小蘿卜  
本篇文章主要介紹了Vue 2.0的數(shù)據(jù)依賴實現(xiàn)原理代碼簡析,主要從初始化的數(shù)據(jù)層面上分析了Vue是如何管理依賴來到達數(shù)據(jù)的動態(tài)響應(yīng),有興趣的可以了解一下

首先讓我們從最簡單的一個實例Vue入手:

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

通過查閱文檔,我們可以知道這個options可以接受:

  1. 選項/數(shù)據(jù)
    1. data
    2. props
    3. propsData(方便測試使用)
    4. computed
    5. methods
    6. watch
  2. 選項 / DOM
  3. 選項 / 生命周期鉤子
  4. 選項 / 資源
  5. 選項 / 雜項

具體未展開的內(nèi)容請自行查閱相關(guān)文檔,接下來讓我們來看看傳入的選項/數(shù)據(jù)是如何管理數(shù)據(jù)之間的相互依賴的。

  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這個構(gòu)造函數(shù)去實例化了一個vue實例app。傳入了props, data, watch, methods等屬性。在實例化的過程中,Vue提供的構(gòu)造函數(shù)就使用我們傳入的options去完成數(shù)據(jù)的依賴管理,初始化的過程只有一次,但是在你自己的程序當(dāng)中,數(shù)據(jù)的依賴管理的次數(shù)不止一次。

那Vue的構(gòu)造函數(shù)到底是怎么實現(xiàn)的呢?Vue

// 構(gòu)造函數(shù)
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)

當(dāng)我們調(diào)用new Vue的時候,事實上就調(diào)用的Vue原型上的_init方法.

// 原型上提供_init方法,新建一個vue實例并傳入options參數(shù)
 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)   // 初始化渲染函數(shù),在vm上綁定$createElement方法
  callHook(vm, 'beforeCreate') // 鉤子函數(shù)的執(zhí)行, beforeCreate
  initInjections(vm) // resolve injections before data/props
  initState(vm)   // Observe data添加對data的監(jiān)聽, 將data轉(zhuǎn)化為getters/setters
  initProvide(vm) // resolve provide after data/props
  callHook(vm, 'created') // 鉤子函數(shù)的執(zhí)行, created

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

其中在this._init()方法中調(diào)用initState(vm),完成對vm這個實例的數(shù)據(jù)的監(jiān)聽,也是本文所要展開說的具體內(nèi)容。

export function initState (vm: Component) {
 // 首先在vm上初始化一個_watchers數(shù)組,緩存這個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的時候,在構(gòu)造函數(shù)里面?zhèn)魅氲膐ptions中有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的值都轉(zhuǎn)化為了getter/setter,并返回一個observer
  const value = validateProp(key, propsOptions, propsData, vm)
  
  // 將這個key對應(yīng)的值轉(zhuǎn)化為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轉(zhuǎn)化為proxyGetter/proxySetter, 并掛載到vm實例上,可以通過app._props[key]這種形式去訪問
  if (!(key in vm)) {
   proxy(vm, `_props`, key)
  }
 }
 observerState.shouldConvert = true
}

接下來看下validateProp(key, propsOptions, propsData, vm)方法內(nèi)部到底發(fā)生了什么。

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類型的數(shù)據(jù)
 // 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的所有屬性轉(zhuǎn)化為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 則調(diào)用def.call(vm)
 // 否則就返回default屬性對應(yīng)的值
 return typeof def === 'function' && getType(prop.type) !== 'Function'
  ? def.call(vm)
  : def
}

Vue提供了一個observe方法,在其內(nèi)部實例化了一個Observer類,并返回Observer的實例。每一個Observer實例對應(yīng)記錄了props中這個的default value的所有依賴(僅限object類型),這個Observer實際上就是一個觀察者,它維護了一個數(shù)組this.subs = []用以收集相關(guān)的subs(訂閱者)(即這個觀察者的依賴)。通過將default value轉(zhuǎn)化為getter/setter形式,同時添加一個自定義__ob__屬性,這個屬性就對應(yīng)Observer實例。

說起來有點繞,還是讓我們看看我們給的demo里傳入的options配置:

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

在往上數(shù)的第二段代碼里面的方法obervse(value),即對{key1: 'a', key2: {a: 'b'}}進行依賴的管理,同時將這個obj所有的屬性值都轉(zhuǎn)化為getter/setter形式。此外,Vue還會將props屬性都代理到vm實例上,通過vm.key1,vm.key2就可以訪問到這個屬性。

此外,還需要了解下在Vue中管理依賴的一個非常重要的類: Dep

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

在Vue的整個生命周期當(dāng)中,你所定義的響應(yīng)式的數(shù)據(jù)上都會綁定一個Dep實例去管理其依賴。它實際上就是觀察者和訂閱者聯(lián)系的一個橋梁。

剛才談到了對于依賴的管理,它的核心之一就是觀察者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值的相關(guān)依賴
  this.dep = new Dep()
  this.vmCount = 0
  // value其實就是vm._data, 即在vm._data上添加__ob__屬性
  def(value, '__ob__', this)
  // 如果是數(shù)組
  if (Array.isArray(value)) {
   // 首先判斷是否能使用__proto__屬性
   const augment = hasProto
    ? protoAugment
    : copyAugment
   augment(value, arrayMethods, arrayKeys)
   // 遍歷數(shù)組,并將obj類型的屬性改為getter/setter實現(xiàn)
   this.observeArray(value)
  } else {
   // 遍歷obj上的屬性,將每個屬性改為getter/setter實現(xiàn)
   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對應(yīng)的屬性都轉(zhuǎn)化為getter/setters,只能是當(dāng)這個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.
  */
 // 監(jiān)聽array中的item
 observeArray (items: Array<any>) {
  for (let i = 0, l = items.length; i < l; i++) {
   observe(items[i])
  }
 }
}

walk方法里面調(diào)用defineReactive方法:通過遍歷這個object的key,并將對應(yīng)的value轉(zhuǎn)化為getter/setter形式,通過閉包維護一個dep,在getter方法當(dāng)中定義了這個key是如何進行依賴的收集,在setter方法中定義了當(dāng)這個key對應(yīng)的值改變后,如何完成相關(guān)依賴數(shù)據(jù)的更新。但是從源碼當(dāng)中,我們卻發(fā)現(xiàn)當(dāng)getter函數(shù)被調(diào)用的時候并非就一定會完成依賴的收集,其中還有一層判斷,就是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轉(zhuǎn)化為getter/setter
 // childOb將子屬性也轉(zhuǎn)化為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
   // 定義相應(yīng)的依賴
   if (Dep.target) {
    // Dep.target.addDep(this)
    // 即添加watch函數(shù)
    // dep.depend()及調(diào)用了dep.addSub()只不過中間需要判斷是否這個id的dep已經(jīng)被包含在內(nèi)了
    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)
   // 相應(yīng)的依賴進行更新
   dep.notify()
  }
 })
}

在上文中提到了Dep類是鏈接觀察者和訂閱者的橋梁。同時在Dep的實現(xiàn)當(dāng)中還有一個非常重要的屬性就是Dep.target,它事實就上就是一個訂閱者,只有當(dāng)Dep.target(訂閱者)存在的時候,調(diào)用屬性的getter函數(shù)的時候才能完成依賴的收集工作。

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是如何來實現(xiàn)訂閱者的呢?Vue里面定義了一個類: Watcher,在Vue的整個生命周期當(dāng)中,會有4類地方會實例化Watcher:

  1. Vue實例化的過程中有watch選項
  2. Vue實例化的過程中有computed計算屬性選項
  3. Vue原型上有掛載$watch方法: Vue.prototype.$watch,可以直接通過實例調(diào)用this.$watch方法
  4. Vue生成了render函數(shù),更新視圖時
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接收的參數(shù)當(dāng)中expOrFn定義了用以獲取watcher的getter函數(shù)。expOrFn可以有2種類型:string或function.若為string類型,首先會通過parsePath方法去對string進行分割(僅支持.號形式的對象訪問)。在除了computed選項外,其他幾種實例化watcher的方式都是在實例化過程中完成求值及依賴的收集工作:this.value = this.lazy ? undefined : this.get().在Watcher的get方法中:

!!!前方高能

get () {
 // pushTarget即設(shè)置當(dāng)前的需要被執(zhí)行的watcher
  pushTarget(this)
  let value
  const vm = this.vm
  if (this.user) {
   try {
    // $watch(function () {})
    // 調(diào)用this.getter的時候,觸發(fā)了屬性的getter函數(shù)
    // 在getter中進行了依賴的管理
    value = this.getter.call(vm, vm)
    console.log(value)
   } catch (e) {
    handleError(e, vm, `getter for watcher "${this.expression}"`)
   }
  } else {
   // 如果是新建模板函數(shù),則會動態(tài)計算模板與data中綁定的變量,這個時候就調(diào)用了getter函數(shù),那么就完成了dep的收集
   // 調(diào)用getter函數(shù),則同時會調(diào)用函數(shù)內(nèi)部的getter的函數(shù),進行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當(dāng)中Dep.target = 當(dāng)前這個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()開始執(zhí)行后,首先會初始化props的屬性為getter/setter函數(shù),然后在進行initWatch初始化的時候,這個時候初始化watcher實例,并調(diào)用get()方法,設(shè)置Dep.target = 當(dāng)前這個watcher實例,進而到value = this.getter.call(vm, vm)的操作。在調(diào)用this.getter.call(vm, vm)的方法中,便會訪問props選項中的a屬性即其getter函數(shù)。在a屬性的getter函數(shù)執(zhí)行過程中,因為Dep.target已經(jīng)存在,那么就進入了依賴收集的過程:

if (Dep.target) {
  // Dep.target.addDep(this)
  // 即添加watch函數(shù)
  // dep.depend()及調(diào)用了dep.addSub()只不過中間需要判斷是否這個id的dep已經(jīng)被包含在內(nèi)了
  dep.depend()
  // childOb也添加依賴
  if (childOb) {
   childOb.dep.depend()
  }
  if (Array.isArray(value)) {
   dependArray(value)
  }
 }

dep是一開始初始化的過程中,這個屬性上的dep屬性。調(diào)用dep.depend()函數(shù):

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

Dep.target也就剛才的那個watcher實例,這里也就相當(dāng)于調(diào)用了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)
   }
  }
 }

這個時候依賴完成了收集,當(dāng)你去修改a屬性的值時,會調(diào)用a屬性的setter函數(shù),里面會執(zhí)行dep.notify(),它會遍歷所有的訂閱者,然后調(diào)用訂閱者上的update函數(shù)。

initData過程和initProps類似,具體可參見源碼。

initComputed

以上就是在initProps過程中Vue是如何進行依賴收集的,initData的過程和initProps類似,下來再來看看initComputed的過程.
在computed屬性初始化的過程當(dāng)中,會為每個屬性實例化一個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函數(shù)
  // 如果computed為非function.則可以單獨為這個屬性定義getter/setter屬性
  let getter = typeof userDef === 'function' ? userDef : userDef.get
  // create internal watcher for the computed property.
  // lazy屬性為true
  // 注意這個地方傳入的getter參數(shù)
  // 實例化的過程當(dāng)中不去完成依賴的收集工作
  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。當(dāng)你訪問computed屬性的時候調(diào)用getter函數(shù):

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屬性是否需要重新求值,因為在上一輪的依賴收集的過程當(dāng)中,觀察者已經(jīng)將這個watcher添加到依賴數(shù)組當(dāng)中了,如果觀察者發(fā)生了變化,就會dep.notify(),通知所有的watcher,而對于computed的watcher接收到變化的請求后,會將watcher.dirty = true即表明觀察者發(fā)生了變化,當(dāng)再次調(diào)用computed屬性的getter函數(shù)的時候便會重新計算,否則還是使用之前緩存的值。

initWatch

initWatch的過程中其實就是實例化new Watcher完成觀察者的依賴收集的過程,在內(nèi)部的實現(xiàn)當(dāng)中是調(diào)用了原型上的Vue.prototype.$watch方法。這個方法也適用于vm實例,即在vm實例內(nèi)部調(diào)用this.$watch方法去實例化watcher,完成依賴的收集,同時監(jiān)聽expOrFn的變化。

總結(jié):

以上就是在Vue實例初始化的過程中實現(xiàn)依賴管理的分析。大致的總結(jié)下就是:

  1. initState的過程中,將props,computed,data等屬性通過Object.defineProperty來改造其getter/setter屬性,并為每一個響應(yīng)式屬性實例化一個observer觀察者。這個observer內(nèi)部dep記錄了這個響應(yīng)式屬性的所有依賴。
  2. 當(dāng)響應(yīng)式屬性調(diào)用setter函數(shù)時,通過dep.notify()方法去遍歷所有的依賴,調(diào)用watcher.update()去完成數(shù)據(jù)的動態(tài)響應(yīng)。

這篇文章主要從初始化的數(shù)據(jù)層面上分析了Vue是如何管理依賴來到達數(shù)據(jù)的動態(tài)響應(yīng)。下一篇文章來分析下Vue中模板中的指令和響應(yīng)式數(shù)據(jù)是如何關(guān)聯(lián)來實現(xiàn)由數(shù)據(jù)驅(qū)動視圖,以及數(shù)據(jù)是如何響應(yīng)視圖變化的。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

  • vue學(xué)習(xí)筆記五:在vue項目里面使用引入公共方法詳解

    vue學(xué)習(xí)筆記五:在vue項目里面使用引入公共方法詳解

    這篇文章主要介紹了在vue項目里面使用引入公共方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Vue3用Proxy替代defineProperty的原因

    Vue3用Proxy替代defineProperty的原因

    vue的人都知道,vue3里面使用了proxy替換了defineProperty,本文主要介紹了Vue3用Proxy替代defineProperty的原因,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • Vue js with語句原理及用法解析

    Vue js with語句原理及用法解析

    這篇文章主要介紹了Vue js with語句原理及用法解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • 詳解使用jest對vue項目進行單元測試

    詳解使用jest對vue項目進行單元測試

    這篇文章主要介紹了詳解使用jest對vue項目進行單元測試,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • axios無法加載響應(yīng)數(shù)據(jù):no?data?found?for?resource?with?given?identifier報錯解決

    axios無法加載響應(yīng)數(shù)據(jù):no?data?found?for?resource?with?given?i

    最近在在做一個小查詢功能的時候踩了一個坑,所以這篇文章主要給大家介紹了關(guān)于axios無法加載響應(yīng)數(shù)據(jù):no?data?found?for?resource?with?given?identifier報錯的解決方法,需要的朋友可以參考下
    2022-11-11
  • vue函數(shù)input輸入值請求時延遲1.5秒請求問題

    vue函數(shù)input輸入值請求時延遲1.5秒請求問題

    這篇文章主要介紹了vue函數(shù)input輸入值請求時延遲1.5秒請求問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Vue倒計時3秒后返回首頁Demo(推薦)

    Vue倒計時3秒后返回首頁Demo(推薦)

    這篇文章主要介紹了Vue倒計時3秒后返回首頁Demo,倒計時結(jié)束后要清除計時器,防止內(nèi)存泄漏,本文通過示例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2023-11-11
  • 使用vue-aplayer插件時出現(xiàn)的問題的解決

    使用vue-aplayer插件時出現(xiàn)的問題的解決

    這篇文章主要介紹了使用vue-aplayer插件時出現(xiàn)的問題的解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • vue3解決跨域問題詳細代碼親測有效

    vue3解決跨域問題詳細代碼親測有效

    跨域,跨的是不同域,也就是協(xié)議或主機或或端口號不同造成的現(xiàn)象,本文給大家分享vue3解決跨域問題詳細代碼親測有效,感興趣的朋友跟隨小編一起看看吧
    2022-11-11
  • 一文徹底搞懂Vue中scoped和/deep/原理

    一文徹底搞懂Vue中scoped和/deep/原理

    在Vue中,有兩種常用的CSS選擇器,用于修改組件樣式:scoped?和?/deep/(或?::v-deep),它們都是為了實現(xiàn)樣式的作用域,本文小編就來分別給大家介紹一下這兩種選擇器的原理,需要的朋友可以參考下
    2023-08-08

最新評論