摘要:的響應式數據綁定原文地址關鍵詞觀察者模式中的響應式數據綁定是通過數據劫持和觀察者模式來實現的。
vue的響應式數據綁定
原文地址:https://github.com/HolyZheng/...
關鍵詞:Object.defineProperty、觀察者模式
vue中的響應式數據綁定是通過數據劫持和觀察者模式來實現的。當前學習源碼為vue2.0
源碼關鍵目錄
src |---core | |---instance | |---init.js | |---state.js | |---observer | |---dep.js | |---watcher.js
當我們實例化一個vue應用的時候,會伴隨著各種的初始化工作,相關的初始化工作代碼在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) }
在這里可以看到對state的初始化工作initState()
// src/core/instance/state.js export function initState (vm: Component) { vm._watchers = [] initProps(vm) initData(vm) initComputed(vm) initMethods(vm) initWatch(vm) }
可以看到這里有對各種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++ }
這里做了一點判斷,判斷data方法是否返回的是一個對象,以及props中是否有與data中重名的屬性,最后會調用observe對data進行監聽,看一下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 }
可已看到這里也是做了一點判斷,如果有__ob__屬性的話就用它,或者如果data是數組或對象或可擴展對象的話,就為它新建一個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) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } } }
判斷data是不是數組,如果是數組就對數組元素再去調用observe方法做同樣的處理,如果不是,就調用walk去劫持該數據,對數據的劫持主要再defineReactive方法中,正如函數名,讓數據變得響應式??匆幌耫efineReactive方法
// src/core/observer/index.js export function defineReactive ( obj: Object, key: string, val: any, customSetter?: Function ) { const dep = new Dep() // data中的每一個成員都有一個對應的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 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() // 發布通知 } }) }
遍歷狀態,修改狀態的getter和setter,當頁面上對應狀態被首次渲染的時候,會為頁面上每一個使用到data的地方新建一個watcher,并將當前watcher保存到全局變量Dep.target中,在對應data的getter中就會調用Dep.depend方法,將當前的watcher添加到當前的Dep中,一個Dep對應一個或多個watcher,著取決于,此狀態被使用的數量。當data被修改時,對應的setter就會被觸發,會調用對應的Dep中的notify方法,通知所有觀察者,進行更新。
這里出現了兩個定的類:Dep和Watcher,其中Dep管理觀察者,Wathcer代表觀察者
先看一下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) { // 調用當前target,也就是正在處理的watcher的addDep方法,并把此Dep傳進去 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)) { // 將當前watcher添加到當前的Dep中 dep.addSub(this) } } } ... }總結
vue的響應式數據綁定主要依賴Object.defineProperty和觀察者模式。
在我們新建一個vue實例的時候,做一系列的初始化工作,這部分的邏輯集中在src文件夾下的core文件夾下的instance和observer文件夾內
響應式數據綁定是在狀態的初始化階段完成的,在initState方法中的initData中進行data的數據綁定。
在initData中調用observe方法,為該data新建一個Observer類,然后最終調用為data中的每一個成員調用walk方法,在walk中通過defineReactive方法劫持當前數據
在defineReactive中通過Object.defineProperty去修改數據的getter和setter
在頁面渲染的時候,頁面上每一個用到data的地方都會生成一個watcher,并將它保存到全局變量Dep.target中,watcher改變每一個觀察者,Dep用來管理觀察者。
然后在data的getter中將調用Dep的depend方法,將Dep.target中的watcher添加到此data對應的Dep中,完成依賴收集
在data被修改的時候,對應data的setter方法就會被出動,會調用Dep.notify()方法發布通知,調用每個watcher的uptade方法進行更新。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95823.html
摘要:雙向數據綁定指的是,將對象屬性變化與視圖的變化相互綁定。數據雙向綁定已經了解到是通過數據劫持的方式來做數據綁定的,其中最核心的方法便是通過來實現對屬性的劫持,達到監聽數據變動的目的。和允許觀察數據的更改并觸發更新。 1 MVVM 雙向數據綁定指的是,將對象屬性變化與視圖的變化相互綁定。換句話說,如果有一個擁有name屬性的user對象,與元素的內容綁定,當給user.name賦予一個新...
摘要:執行的時候,會綁定上下文對象為組件實例于是中的就能取到組件實例本身,的代碼塊頂層作用域就綁定為了組件實例于是內部變量的訪問,就會首先訪問到組件實例上。其中的獲取,就會先從組件實例上獲取,相當于。 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 如果你覺得...
摘要:前言最近在學習框架的基本原理,看了一些技術博客以及一些對源碼的簡單實現,對數據代理數據劫持模板解析變異數組方法雙向綁定有了更深的理解。 前言 最近在學習vue框架的基本原理,看了一些技術博客以及一些對vue源碼的簡單實現,對數據代理、數據劫持、模板解析、變異數組方法、雙向綁定有了更深的理解。于是乎,嘗試著去實踐自己學到的知識,用vue的一些基本原理實現一個簡單的todo-list,完成...
摘要:假如你通過閱讀源碼,掌握了對的實現原理,對生態系統有了充分的認識,那你會在面試環節游刃有余,達到晉級阿里的技術功底,從而提高個人競爭力,面試加分更容易拿。 前言 一年一度緊張刺激的高考開始了,與此同時,我也沒閑著,奔走在各大公司的前端面試環節,不斷積累著經驗,一路升級打怪。 最近兩年,太原作為一個準二線城市,各大互聯網公司的技術棧也在升級換代,假如你在太原面試前端崗位,而你的技術庫里若...
閱讀 1660·2021-11-16 11:41
閱讀 2458·2021-11-08 13:14
閱讀 3106·2019-08-29 17:16
閱讀 3079·2019-08-29 16:30
閱讀 1843·2019-08-29 13:51
閱讀 356·2019-08-23 18:38
閱讀 3223·2019-08-23 17:14
閱讀 630·2019-08-23 15:09