国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

vue中的數(shù)據(jù)綁定原理的實(shí)現(xiàn)

2019-11-19 13:34:59
字體:
供稿:網(wǎng)友

本文主要介紹了vue中的數(shù)據(jù)綁定原理的實(shí)現(xiàn),分享給大家,也給自己留個筆記,具體如下:


vue中的響應(yīng)式數(shù)據(jù)綁定是通過數(shù)據(jù)劫持和觀察者模式來實(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í)例化一個vue應(yīng)用的時(shí)候,會伴隨著各種的初始化工作,相關(guān)的初始化工作代碼在init.js文件中

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) { ... initLifecycle(vm) initEvents(vm) callHook(vm, 'beforeCreate') initState(vm) callHook(vm, 'created') initRender(vm)}

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

// src/core/instance/state.jsexport function initState (vm: Component) { vm._watchers = [] initProps(vm) initData(vm) initComputed(vm) initMethods(vm) initWatch(vm)}

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

// src/core/instance/state.jsfunction 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方法是否返回的是一個對象,以及props中是否有與data中重名的屬性,最后會調(diào)用observe對data進(jìn)行監(jiān)聽,看一下observe

// src/core/observer/index.jsexport 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ò)展對象的話,就為它新建一個Observer,看一下Observer

// src/core/observer/index.jsexport 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ù)組就對數(shù)組元素再去調(diào)用observe方法做同樣的處理,如果不是,就調(diào)用walk去劫持該數(shù)據(jù),對數(shù)據(jù)的劫持主要再defineReactive方法中,正如函數(shù)名,讓數(shù)據(jù)變得響應(yīng)式??匆幌耫efineReactive方法

// src/core/observer/index.jsexport function defineReactive ( obj: Object, key: string, val: any, customSetter?: Function) { const dep = new Dep()// data中的每一個成員都有一個對應(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īng)狀態(tài)被首次渲染的時(shí)候,會為頁面上每一個使用到data的地方新建一個watcher,并將當(dāng)前watcher保存到全局變量Dep.target中,在對應(yīng)data的getter中就會調(diào)用Dep.depend方法,將當(dāng)前的watcher添加到當(dāng)前的Dep中,一個Dep對應(yīng)一個或多個watcher,著取決于,此狀態(tài)被使用的數(shù)量。當(dāng)data被修改時(shí),對應(yīng)的setter就會被觸發(fā),會調(diào)用對應(yīng)的Dep中的notify方法,通知所有觀察者,進(jìn)行更新。

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

先看一下Dep

// src/core/observer/dep.jsexport 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.jsexport 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. 在我們新建一個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新建一個Observer類,然后最終調(diào)用為data中的每一個成員調(diào)用walk方法,在walk中通過defineReactive方法劫持當(dāng)前數(shù)據(jù)
  4. 在defineReactive中通過Object.defineProperty去修改數(shù)據(jù)的getter和setter
  5. 在頁面渲染的時(shí)候,頁面上每一個用到data的地方都會生成一個watcher,并將它保存到全局變量Dep.target中,watcher改變每一個觀察者,Dep用來管理觀察者。
  6. 然后在data的getter中將調(diào)用Dep的depend方法,將Dep.target中的watcher添加到此data對應(yīng)的Dep中,完成依賴收集
  7. 在data被修改的時(shí)候,對應(yīng)data的setter方法就會被出動,會調(diào)用Dep.notify()方法發(fā)布通知,調(diào)用每個watcher的uptade方法進(jìn)行更新。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 年辖:市辖区| 永和县| 石柱| 府谷县| 鹤岗市| 抚顺市| 辽阳市| 海南省| 肥城市| 宝兴县| 山西省| 连江县| 沙湾县| 黑河市| 邢台市| 侯马市| 英超| 于田县| 天津市| 敦煌市| 玉田县| 湖北省| 瑞丽市| 梁山县| 永善县| 扶沟县| 寻甸| 诸城市| 鹿泉市| 灵丘县| 盐源县| 博乐市| 宝应县| 晋江市| 金溪县| 砀山县| 米脂县| 东城区| 万载县| 监利县| 冕宁县|