vue框架的兩個抽象核心:虛擬DOM和相應式數據原理vue
關於虛擬DOM的核心算法,咱們上一章已經基本解析過了,詳細的見
React && VUE Virtual Dom的Diff算法統一之路 snabbdom.js解讀react
關於響應式數據原理,咱們先看張圖
你 ‘ (4).png
算法
具體來說,要分如下幾步:segmentfault
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]) } } }
首先咱們觀察到,new Observer()的時候,會進行類型的判斷數組
if (Array.isArray(value)) { const augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) this.observeArray(value) } else { this.walk(value) }
若是是數組類型則會遞歸調用,創建依賴體系
不然則調用walk()函數去利用Object.defineProperty簡歷依賴框架
那麼defineReactive是如何實現的呢,以下dom
//省略了部分代碼 export function defineReactive ( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean ) { const dep = new Dep() ... Object.defineProperty(obj, key, { ... get: function reactiveGetter () { ... dep.depend() ... return value }, set: function reactiveSetter (newVal) { ... dep.notify() } }) }
攔截器會分別在getter和setter上設置中間件去維護dep數組中的依賴關係函數
字面意思很是明顯,分別用於增長依賴和通知依賴變化關係ui
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) { Dep.target.addDep(this) } } notify () { // stabilize the subscriber list first const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } }
Dep 就是一個 Watcher 所對應的數據依賴,在這個對象中也存有一個 subs 數組,用來保存和這個依賴有關的 Watcher。其成員函數最主要的是 depend 和 notify ,前者用來設置某個 Watcher 的依賴,後者則用來通知與這個依賴相關的 Watcher 來運行其回調函數。this
咱們知道,Dom 上經過指令或者雙大括號綁定的數據,會爲數據進行添加觀察者watcher,當實例化Watcher的時候 會觸發屬性的getter方法,此時會調用dep.depend()。咱們看一下depend方法:
depend () { if (Dep.target) { Dep.target.addDep(this) } }
Dep.target 是什麼東西呢?其實在進行Watcher實例化的時候,會調用內部的get函數,開始爲他初始化
其中pushTarget 方法就是爲Dep.target綁定此watcher實例,因此Dep.target.addDep(this)也就是執行此實例中的addDep方法
addDep (dep: Dep) { ... dep.addSub(this) }
這樣便爲咱們的dep實例添加了一個watcher實例。
接着當咱們更新data的時候,會觸發他的set方法,執行dep.notify()函數:
notify () { // stabilize the subscriber list first const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } }
這裏也就是遍歷dep中收集到的watcher實例,進行update()。也就是進行數據更新操做。這也就是簡單的數據響應式,其實還須要注意的是: 當數據的getter觸發後,會收集依賴,但也不是全部的觸發方式都會收集依賴,只有經過watcher觸發的getter會收集依賴:if (Dep.target) { dep.depend() },而所謂的被收集的依賴就是當前watcher,DOM中的數據必須經過watcher來綁定,只經過watcher來讀取。
最後付一個函數timeline
的.png