簡單實(shí)現(xiàn)vue中的依賴收集與響應(yīng)的方法
開始
聲明一個對象man,可以視為vue中的data
let man = {
height: 180,
weight: 70,
wealth: 100000000
}
添加Observer
作用在于將參數(shù)對象的屬性變?yōu)轫憫?yīng)式,只要對象的屬性被讀取或者被修改都能觀察到。然后新建一個Observer實(shí)例,將man作為參數(shù)扔進(jìn)去。這里的proxyData是將man的屬性代理到以man為參數(shù)的Observer實(shí)例上去。
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)
這時(shí)打印一下man

現(xiàn)在man的屬性都是由Observer實(shí)例所對應(yīng)的屬性的getter來返回,只有在查看時(shí)會被觸發(fā)

對man的屬性進(jìn)行修改也會觸發(fā)實(shí)例對應(yīng)屬性的setter

添加Watcher
現(xiàn)在的Watcher有點(diǎn)像vue中的computed,實(shí)際上就是定義一個計(jì)算屬性,這個計(jì)算屬性依賴于前面man中的某些屬性,由他們計(jì)算而得。
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('計(jì)算屬性不可被修改!')
}
})
}
}
new Watcher(man, 'strength', () => {
let {height, weight} = man
if (height > 160 && weight > 70) return 'strong'
return 'weak'
})

看起來沒什么問題,所依賴的屬性如果變了,計(jì)算屬性只要再被查看(get方法)一次就可以更新了。但vue中的視圖渲染是實(shí)時(shí)的,視圖層依賴于數(shù)據(jù)層,數(shù)據(jù)變化了,視圖層也會跟著變化,不需要手動更新。類比到這個例子就是計(jì)算屬性如何才能在其所依賴的屬性發(fā)生變化時(shí)被通知從而觸發(fā)應(yīng)有的事件?
這時(shí)我們先給Watcher加多一個callback,用于處理當(dāng)依賴的數(shù)據(jù)被修改時(shí),我這個計(jì)算屬性該怎么響應(yīng)
比如當(dāng)依賴被修改時(shí),我們就把這個計(jì)算屬性的值打印出來
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} !`)
})
一切都準(zhǔn)備好了,接下來就是該如何實(shí)現(xiàn)?
我們先看下Watcher中g(shù)etVal這個方法
getVal(obj, prop, computed, callback) {
Object.defineProperty(obj, prop, {
get() {
console.log(`computed屬性 - ${prop}被讀??!`)
return computed()
},
set() {
console.error('計(jì)算屬性不可被修改!')
}
})
}
當(dāng)我們查看計(jì)算屬性時(shí),會調(diào)用computed這個方法,相當(dāng)于查看了其所依賴的height和weight屬性,而在上面我們已經(jīng)讓man的所有屬性都擁有了get方法,即他們被查看時(shí)我們是不是可以把callback塞給他們?
這時(shí)候我們引進(jìn)一個橋梁,來連接Watcher和Observer。
添加Dep
Dep的用處在于當(dāng)某一個屬性(以下稱‘自己')被依賴了,將依賴自己的粉絲(們)--也就是Watcher(s),收集起來,假如自己發(fā)生了變化,能夠及時(shí)通知粉絲們。
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方法了。這時(shí)我們改一下Watcher中的getVal
getVal(obj, prop, computed, callback) {
Object.defineProperty(obj, prop, {
get() {
Dep.target = callback
console.log(`computed屬性 - ${prop}被讀??!`)
return computed()
},
set() {
console.error('計(jì)算屬性不可被修改!')
}
})
}
在計(jì)算屬性被查看時(shí),將callback賦值給Dep.target,接下來就會調(diào)用其所依賴屬性的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} - 被修改!`)
}
})
}
這時(shí)watcher的callback都被依賴屬性給收集起來了,當(dāng)依賴屬性發(fā)生變化時(shí)只要去運(yùn)行這些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() // 運(yùn)行所有callback
}
})
}
運(yùn)行看看

我們加多一個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中的一個依賴對應(yīng)多個Watcher
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue.js移動端app之上拉加載以及下拉刷新實(shí)戰(zhàn)
這篇文章主要介紹了vue.js移動端app之上拉加載以及下拉刷新實(shí)戰(zhàn),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-09-09
ArcGis?API?for?js在vue.js中的使用示例詳解
這篇文章主要為大家介紹了ArcGis?API?for?js在vue.js中的使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
ant?菜單組件報(bào)錯Cannot?read?property?‘isRootMenu‘?of?undefin
這篇文章主要介紹了ant?菜單組件報(bào)錯Cannot?read?property?‘isRootMenu‘?of?undefined解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
vue路由跳轉(zhuǎn)攜帶參數(shù)的方式總結(jié)
我們知道在vue中每個頁面都需要在路由中聲明,下面這篇文章主要給大家介紹了關(guān)于vue路由跳轉(zhuǎn)攜帶參數(shù)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10
vue-cli和v-charts實(shí)現(xiàn)可視化圖表過程解析
這篇文章主要介紹了vue-cli和v-charts實(shí)現(xiàn)可視化圖表過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
一次前端Vue項(xiàng)目國際化解決方案的實(shí)戰(zhàn)記錄
這篇文章主要給大家介紹了關(guān)于前端Vue項(xiàng)目國際化解決方案的實(shí)戰(zhàn)記錄,以上只是一部分Vue項(xiàng)目開發(fā)中遇到的典型問題和解決方案,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07

