簡單實(shí)現(xiàn)vue中的依賴收集與響應(yīng)的方法
開始
聲明一個(gè)對象man,可以視為vue中的data
let man = { height: 180, weight: 70, wealth: 100000000 }
添加Observer
作用在于將參數(shù)對象的屬性變?yōu)轫憫?yīng)式,只要對象的屬性被讀取或者被修改都能觀察到。然后新建一個(gè)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í)際上就是定義一個(gè)計(jì)算屬性,這個(gè)計(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ù)變化了,視圖層也會跟著變化,不需要手動更新。類比到這個(gè)例子就是計(jì)算屬性如何才能在其所依賴的屬性發(fā)生變化時(shí)被通知從而觸發(fā)應(yīng)有的事件?
這時(shí)我們先給Watcher加多一個(gè)callback,用于處理當(dāng)依賴的數(shù)據(jù)被修改時(shí),我這個(gè)計(jì)算屬性該怎么響應(yīng)
比如當(dāng)依賴被修改時(shí),我們就把這個(gè)計(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這個(gè)方法
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這個(gè)方法,相當(dāng)于查看了其所依賴的height和weight屬性,而在上面我們已經(jīng)讓man的所有屬性都擁有了get方法,即他們被查看時(shí)我們是不是可以把callback塞給他們?
這時(shí)候我們引進(jìn)一個(gè)橋梁,來連接Watcher和Observer。
添加Dep
Dep的用處在于當(dāng)某一個(gè)屬性(以下稱‘自己')被依賴了,將依賴自己的粉絲(們)--也就是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)行看看
我們加多一個(gè)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中的一個(gè)依賴對應(yīng)多個(gè)Watcher
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
解決ant design vue中樹形控件defaultExpandAll設(shè)置無效的問題
這篇文章主要介紹了解決ant design vue中樹形控件defaultExpandAll設(shè)置無效的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10fullcalendar日程管理插件月份切換回調(diào)處理方案
這篇文章主要為大家介紹了fullcalendar日程管理插件月份切換回調(diào)處理的方案示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03Vue3+Hooks實(shí)現(xiàn)4位隨機(jī)數(shù)和60秒倒計(jì)時(shí)的示例代碼
Vue3的Hooks是一種新的 API,本文主要介紹了Vue3+Hooks實(shí)現(xiàn)4位隨機(jī)數(shù)和60秒倒計(jì)時(shí)的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-04-04Vue?element-ui中表格過長內(nèi)容隱藏顯示的實(shí)現(xiàn)方式
在Vue項(xiàng)目中,使用ElementUI渲染表格數(shù)據(jù)時(shí),如果某一個(gè)列數(shù)值長度超過列寬,會默認(rèn)換行,造成顯示不友好,下面這篇文章主要給大家介紹了關(guān)于Vue?element-ui中表格過長內(nèi)容隱藏顯示的實(shí)現(xiàn)方式,需要的朋友可以參考下2022-09-09關(guān)于vue的npm run dev和npm run build的區(qū)別介紹
這篇文章主要介紹了關(guān)于vue的npm run dev和npm run build的區(qū)別介紹,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01Vue axios全局?jǐn)r截 get請求、post請求、配置請求的實(shí)例代碼
這篇文章主要介紹了Vue axios全局?jǐn)r截 get請求、post請求、配置請求的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11Vue 中獲取當(dāng)前時(shí)間并實(shí)時(shí)刷新的實(shí)現(xiàn)代碼
這篇文章主要介紹了Vue 中獲取當(dāng)前時(shí)間并實(shí)時(shí)刷新,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05Vue2?與?Vue3?的數(shù)據(jù)綁定原理及實(shí)現(xiàn)
這篇文章主要介紹了Vue2與Vue3的數(shù)據(jù)綁定原理及實(shí)現(xiàn),數(shù)據(jù)綁定是一種把用戶界面元素的屬性綁定到特定對象上面并使其同步的機(jī)制,使開發(fā)人員免于編寫同步視圖模型和視圖的邏輯2022-09-09