聲明一個對象man,能夠視爲vue中的datavue
let man = { height: 180, weight: 70, wealth: 100000000 }
做用在於將參數對象的屬性變爲響應式,只要對象的屬性被讀取或者被修改都能觀察到。而後新建一個Observer實例,將man做爲參數扔進去。這裏的proxyData是將man的屬性代理到以man爲參數的Observer實例上去。this
class Observer { constructor(obj) { this.walk(obj) } walk(obj) { Object.keys(obj).forEach(prop => { this[prop] = obj[prop] this.proxyData(obj, prop) this.defineReactive(this, prop, obj[prop]) }) } proxyData(obj, prop) { let _this = this Object.defineProperty(obj, prop, { get() { return _this[prop] }, set(newVal) { _this[prop] = newVal } }) } defineReactive(obj, prop, val) { Object.defineProperty(obj, prop, { get() { console.log(`${prop} - 被讀取!`) return val }, set(newVal) { if (newVal == val) return val = newVal console.log(`${prop} - 被修改!`) } }) } } new Observer(man)
這時打印一下manspa
如今man的屬性都是由Observer實例所對應的屬性的getter來返回,只有在查看時會被觸發代理
對man的屬性進行修改也會觸發實例對應屬性的settercode
如今的Watcher有點像vue中的computed,實際上就是定義一個計算屬性,這個計算屬性依賴於前面man中的某些屬性,由他們計算而得。server
class Watcher { constructor(obj, prop, computed) { this.getVal(obj, prop, computed) } getVal(obj, prop, computed) { Object.defineProperty(obj, prop, { get() { console.log(`computed屬性 - ${prop}被讀取!`) return computed() }, set() { console.error('計算屬性不可被修改!') } }) } } new Watcher(man, 'strength', () => { let {height, weight} = man if (height > 160 && weight > 70) return 'strong' return 'weak' })
看起來沒什麼問題,所依賴的屬性若是變了,計算屬性只要再被查看(get方法)一次就能夠更新了。但vue中的視圖渲染是實時的,視圖層依賴於數據層,數據變化了,視圖層也會跟着變化,不須要手動更新。類比到這個例子就是計算屬性如何才能在其所依賴的屬性發生變化時被通知從而觸發應有的事件?對象
這時咱們先給Watcher加多一個callback,用於處理當依賴的數據被修改時,我這個計算屬性該怎麼響應事件
好比當依賴被修改時,咱們就把這個計算屬性的值打印出來rem
class Watcher { constructor(obj, prop, computed, callback) { this.getVal(obj, prop, computed, callback) } new Watcher(man, 'strength', () => { let {height, weight} = man if (height > 160 && weight > 70) return 'strong' return 'weak' }, () => { console.log(`i am so ${man.strength} !`) })
一切都準備好了,接下來就是該如何實現?get
咱們先看下Watcher中getVal這個方法
getVal(obj, prop, computed, callback) { Object.defineProperty(obj, prop, { get() { console.log(`computed屬性 - ${prop}被讀取!`) return computed() }, set() { console.error('計算屬性不可被修改!') } }) }
當咱們查看計算屬性時,會調用computed這個方法,至關於查看了其所依賴的height和weight屬性,而在上面咱們已經讓man的全部屬性都擁有了get方法,即他們被查看時咱們是否是能夠把callback塞給他們?
這時候咱們引進一個橋樑,來鏈接Watcher和Observer。
Dep的用處在於當某一個屬性(如下稱‘本身’)被依賴了,將依賴本身的粉絲(們)--也就是Watcher(s),收集起來,假如本身發生了變化,可以及時通知粉絲們。
class Dep { constructor() { this.deps = [] } getDeps() { if (!Dep.target || this.deps.includes(Dep.target)) return console.log('依賴添加', Dep.target) this.deps.push(Dep.target) } notify() { this.deps.forEach(dep => { dep() }) } }
這裏的Dep.target就是前面所說的callback方法了。這時咱們改一下Watcher中的getVal
getVal(obj, prop, computed, callback) { Object.defineProperty(obj, prop, { get() { Dep.target = callback console.log(`computed屬性 - ${prop}被讀取!`) return computed() }, set() { console.error('計算屬性不可被修改!') } }) }
在計算屬性被查看時,將callback賦值給Dep.target,接下來就會調用其所依賴屬性的getter,咱們只要在getter裏把callback給收集起來就好了。接下來修改依賴屬性的getter方法。
defineReactive(obj, prop, val) { let dep = new Dep() Object.defineProperty(obj, prop, { get() { console.log(`${prop} - 被讀取!`) dep.getDeps() // 依賴收集 return val }, set(newVal) { if (newVal == val) return val = newVal console.log(`${prop} - 被修改!`) } }) }
這時watcher的callback都被依賴屬性給收集起來了,當依賴屬性發生變化時只要去運行這些callback就能夠了。接下來就是修改依賴屬性的setter方法。
defineReactive(obj, prop, val) { let dep = new Dep() Object.defineProperty(obj, prop, { get() { console.log(`${prop} - 被讀取!`) dep.getDeps() return val }, set(newVal) { if (newVal == val) return val = newVal console.log(`${prop} - 被修改!`) dep.notify() // 運行全部callback } }) }
運行看看
咱們加多一個Watcher試試
new Watcher(man, 'isGreat', () => { let {height, weight, wealth} = man if (height > 180 && weight > 70 && wealth > 100000) return 'Great!' return 'not good enough ...' }, () => { console.log(`they say i am ${man.isGreat}`) })
這就是vue中的一個依賴對應多個Watcher