溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

vue項(xiàng)目中數(shù)據(jù)綁定的原理是什么

發(fā)布時(shí)間:2021-01-19 17:30:20 來(lái)源:億速云 閱讀:237 作者:Leah 欄目:web開(kāi)發(fā)

vue項(xiàng)目中數(shù)據(jù)綁定的原理是什么?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

具體如下:

vue項(xiàng)目中數(shù)據(jù)綁定的原理是什么

vue中的響應(yīng)式數(shù)據(jù)綁定是通過(guò)數(shù)據(jù)劫持和觀察者模式來(lái)實(shí)現(xiàn)的。當(dāng)前學(xué)習(xí)源碼為vue2.0

源碼關(guān)鍵目錄

src
|---core
|  |---instance
|     |---init.js
|     |---state.js
|  |---observer
|     |---dep.js
|     |---watcher.js

當(dāng)我們實(shí)例化一個(gè)vue應(yīng)用的時(shí)候,會(huì)伴隨著各種的初始化工作,相關(guān)的初始化工作代碼在init.js文件中

// src/core/instance/init.js

Vue.prototype._init = function (options?: Object) {
 ...
 initLifecycle(vm)
 initEvents(vm)
 callHook(vm, 'beforeCreate')
 initState(vm)
 callHook(vm, 'created')
 initRender(vm)
}

在這里可以看到對(duì)state的初始化工作initState()

// src/core/instance/state.js

export function initState (vm: Component) {
 vm._watchers = []
 initProps(vm)
 initData(vm)
 initComputed(vm)
 initMethods(vm)
 initWatch(vm)
}

可以看到這里有對(duì)各種sate的初始化工作,我們看initData()

// src/core/instance/state.js

function initData (vm: Component) {
 let data = vm.$options.data
 data = vm._data = typeof data === 'function'
  ? data.call(vm)
  : data || {}
 if (!isPlainObject(data)) {
  data = {}
  process.env.NODE_ENV !== 'production' && warn(
   'data functions should return an object.',
   vm
  )
 }
 // proxy data on instance
 const keys = Object.keys(data)
 const props = vm.$options.props
 let i = keys.length
 while (i--) {
  if (props && hasOwn(props, keys[i])) {
   process.env.NODE_ENV !== 'production' && warn(
    `The data property "${keys[i]}" is already declared as a prop. ` +
    `Use prop default value instead.`,
    vm
   )
  } else {
   proxy(vm, keys[i])
  }
 }
 // observe data
 observe(data)
 data.__ob__ && data.__ob__.vmCount++
}

這里做了一點(diǎn)判斷,判斷data方法是否返回的是一個(gè)對(duì)象,以及props中是否有與data中重名的屬性,最后會(huì)調(diào)用observe對(duì)data進(jìn)行監(jiān)聽(tīng),看一下observe

// src/core/observer/index.js

export function observe (value: any): Observer | void {
 if (!isObject(value)) {
  return
 }
 let ob: Observer | void
 if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
  ob = value.__ob__
 } else if (
  observerState.shouldConvert &&
  !config._isServer &&
  (Array.isArray(value) || isPlainObject(value)) &&
  Object.isExtensible(value) &&
  !value._isVue
 ) {
  ob = new Observer(value)
 }
 return ob
}

可已看到這里也是做了一點(diǎn)判斷,如果有__ob__屬性的話就用它,或者如果data是數(shù)組或?qū)ο蠡蚩蓴U(kuò)展對(duì)象的話,就為它新建一個(gè)Observer,看一下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
  this.dep = new Dep()
  this.vmCount = 0
  def(value, '__ob__', this)
  if (Array.isArray(value)) {
   const augment = hasProto
    ? protoAugment
    : copyAugment
   augment(value, arrayMethods, arrayKeys)
   this.observeArray(value)
  } else {
   this.walk(value)
  }
 }

 /**
  * Walk through each property and convert them into
  * getter/setters. This method should only be called when
  * value type is 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.
  */
 observeArray (items: Array<any>) {
  for (let i = 0, l = items.length; i < l; i++) {
   observe(items[i])
  }
 }
}

判斷data是不是數(shù)組,如果是數(shù)組就對(duì)數(shù)組元素再去調(diào)用observe方法做同樣的處理,如果不是,就調(diào)用walk去劫持該數(shù)據(jù),對(duì)數(shù)據(jù)的劫持主要再defineReactive方法中,正如函數(shù)名,讓數(shù)據(jù)變得響應(yīng)式。看一下defineReactive方法

// src/core/observer/index.js

export function defineReactive (
 obj: Object,
 key: string,
 val: any,
 customSetter?: Function
) {
 const dep = new Dep()
// data中的每一個(gè)成員都有一個(gè)對(duì)應(yīng)的Dep,在此閉包創(chuàng)建。

 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

 let childOb = observe(val)
 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function reactiveGetter () {
   const value = getter ? getter.call(obj) : val
   if (Dep.target) {
    dep.depend() // 依賴收集
    if (childOb) {
     childOb.dep.depend()
    }
    if (Array.isArray(value)) {
     for (let e, i = 0, l = value.length; i < l; i++) {
      e = value[i]
      e && e.__ob__ && e.__ob__.dep.depend()
     }
    }
   }
   return value
  },
  set: function reactiveSetter (newVal) {
   const value = getter ? getter.call(obj) : val
   if (newVal === value) {
    return
   }
   if (process.env.NODE_ENV !== 'production' && customSetter) {
    customSetter()
   }
   if (setter) {
    setter.call(obj, newVal)
   } else {
    val = newVal
   }
   childOb = observe(newVal)
   dep.notify() // 發(fā)布通知
  }
 })
}

遍歷狀態(tài),修改狀態(tài)的getter和setter,當(dāng)頁(yè)面上對(duì)應(yīng)狀態(tài)被首次渲染的時(shí)候,會(huì)為頁(yè)面上每一個(gè)使用到data的地方新建一個(gè)watcher,并將當(dāng)前watcher保存到全局變量Dep.target中,在對(duì)應(yīng)data的getter中就會(huì)調(diào)用Dep.depend方法,將當(dāng)前的watcher添加到當(dāng)前的Dep中,一個(gè)Dep對(duì)應(yīng)一個(gè)或多個(gè)watcher,著取決于,此狀態(tài)被使用的數(shù)量。當(dāng)data被修改時(shí),對(duì)應(yīng)的setter就會(huì)被觸發(fā),會(huì)調(diào)用對(duì)應(yīng)的Dep中的notify方法,通知所有觀察者,進(jìn)行更新。

這里出現(xiàn)了兩個(gè)定的類:Dep和Watcher,其中Dep管理觀察者,Wathcer代表觀察者

先看一下Dep

// src/core/observer/dep.js

export default class Dep {
 static target: ?Watcher;
 id: number;
 subs: Array<Watcher>;

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

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

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

 depend () {
  if (Dep.target) {
// 調(diào)用當(dāng)前target,也就是正在處理的watcher的addDep方法,并把此Dep傳進(jìn)去
   Dep.target.addDep(this)
  }
 }

 notify () {
  // stablize the subscriber list first
  const subs = this.subs.slice()
  for (let i = 0, l = subs.length; i < l; i++) {
   subs[i].update()
  }
 }
}

看一下watcher.js

// src/core/observer/watcher.js

export default class Watcher {
...
 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)) {
    // 將當(dāng)前watcher添加到當(dāng)前的Dep中
    dep.addSub(this)
   }
  }
 }
...
}

總結(jié)

vue的響應(yīng)式數(shù)據(jù)綁定主要依賴Object.defineProperty和觀察者模式。

  1. 在我們新建一個(gè)vue實(shí)例的時(shí)候,做一系列的初始化工作,這部分的邏輯集中在src文件夾下的core文件夾下的instance和observer文件夾內(nèi)

  2. 響應(yīng)式數(shù)據(jù)綁定是在狀態(tài)的初始化階段完成的,在initState方法中的initData中進(jìn)行data的數(shù)據(jù)綁定。

  3. 在initData中調(diào)用observe方法,為該data新建一個(gè)Observer類,然后最終調(diào)用為data中的每一個(gè)成員調(diào)用walk方法,在walk中通過(guò)defineReactive方法劫持當(dāng)前數(shù)據(jù)

  4. 在defineReactive中通過(guò)Object.defineProperty去修改數(shù)據(jù)的getter和setter

  5. 在頁(yè)面渲染的時(shí)候,頁(yè)面上每一個(gè)用到data的地方都會(huì)生成一個(gè)watcher,并將它保存到全局變量Dep.target中,watcher改變每一個(gè)觀察者,Dep用來(lái)管理觀察者。

  6. 然后在data的getter中將調(diào)用Dep的depend方法,將Dep.target中的watcher添加到此data對(duì)應(yīng)的Dep中,完成依賴收集

  7. 在data被修改的時(shí)候,對(duì)應(yīng)data的setter方法就會(huì)被出動(dòng),會(huì)調(diào)用Dep.notify()方法發(fā)布通知,調(diào)用每個(gè)watcher的uptade方法進(jìn)行更新。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

vue
AI