您好,登錄后才能下訂單哦!
這篇文章主要介紹了javascript中vue響應(yīng)式原理及依賴收集的示例分析,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
Vue通過設(shè)定對象屬性的setter/getter方法來監(jiān)聽數(shù)據(jù)的變化,通過getter進(jìn)行依賴收集,而每個(gè)setter方法就是一個(gè)觀察者,在數(shù)據(jù)變更的時(shí)候通知訂閱者更新視圖。
將數(shù)據(jù)data變成可觀察的(observable)
那么Vue是如何將所有data下面的屬性變成可觀察的呢?
function obsever(value,cb){ Object.keys(value).forEach((key)=>defineReactive(value,key,value[key],cb)) } function defineReactive(obj,key,val,cb){ Object.defineProperty(obj,key,{ enumerable;true, configurable:true, get:()=>{ /*依賴收集*/ return val; }, set:newVal=>{ val=newVal; cb(); } }) } class Vue{ constructor(options){ this._data = options.data; obsever(this._data,options.render) } } let app = new Vue({ el:'#app', data:{ text:'text', text2:'text2' }, render(){ console.log('render') } })
為了便于理解,首先考慮一種最簡單的情況,不考慮數(shù)組等情況,代碼如上所示。在initData中會(huì)調(diào)用observe這個(gè)函數(shù)將Vue的數(shù)據(jù)設(shè)置成observable的。當(dāng)_data數(shù)據(jù)發(fā)生改變的時(shí)候就會(huì)觸發(fā)set,對訂閱者進(jìn)行回調(diào)(在這里是render)。
那么問題來了,需要對app._data.text操作才會(huì)觸發(fā)set。為了偷懶,我們需要一種方便的方法通過app.text直接設(shè)置就能觸發(fā)set對視圖進(jìn)行重繪。那么就需要到代理。
代理
我們可以在Vue的構(gòu)造函數(shù)constructor中為data執(zhí)行一個(gè)代理proxy。這樣我們就把data上面的屬性代理到了vm實(shí)例上。
_proxy.call(this,options.data);//構(gòu)造函數(shù) //代理 function _proxy(data){ const that = this; Object.keys(data).forEach(key=>{ configurable:true, enumerable:true, get:function proxyGetter(){ return that._data[key] }, set:function proxySetter(val){ that._data[key] = val; } }) }
我們就可以用app.text代替app._data.text了。
為什么要依賴收集
先看下面這段代碼
new Vue({ template:`<p> <span>text1:</span>{{text1}} <span>text2:</span>{{tetx2}} </p>`, data:{ text1:'text1', text2:'text2', text3:'text3' } })
按照上面的響應(yīng)式原理中的方法進(jìn)行綁定則會(huì)出現(xiàn)一個(gè)問題--text3在實(shí)際模板中并沒有被用到,然而當(dāng)text3的數(shù)據(jù)被修改時(shí),同樣會(huì)觸發(fā)text3的setter導(dǎo)致重新執(zhí)行渲染,這顯然不正確。
先說說Dep
當(dāng)對data上的對象進(jìn)行修改值的時(shí)候會(huì)觸發(fā)它的setter,那么取值的時(shí)候自然會(huì)觸發(fā)getter事件,所以我們只要在最開始進(jìn)行一次render,那么所有被渲染所依賴的data中的數(shù)據(jù)就會(huì)被getter收集到Dep的subs中去。在對data中的數(shù)據(jù)進(jìn)行修改的時(shí)候setter只會(huì)觸發(fā)Dep的subs函數(shù)。
定義一個(gè)依賴收集類的Dep。
class Dep{ constructor(){ this.subs = []; } addSub(sub:Watcher){ this.subs.push(sub) } removeSub(sub:Watcher){ remove(this.subs,sub) } notify(){ const subs = this.subs.slice() for(let i = 0;l=subs.length;i<1;i++){ subs[i].update() } } } function remove(arr,item){ if(arr.length){ const index = arr.indexOf(item) if(index>-1){ return arr.splice(index,1) } } }
Watcher
訂閱者,當(dāng)依賴收集的時(shí)候會(huì)addSub到sub中,在修改data中數(shù)據(jù)的時(shí)候會(huì)觸發(fā)dep對象的notify,通知所有Watcher對象去修改對應(yīng)視圖。
class Watcher { constructor (vm, expOrFn, cb, options) { this.cb = cb; this.vm = vm; /*在這里將觀察者本身賦值給全局的target,只有被target標(biāo)記過的才會(huì)進(jìn)行依賴收集*/ Dep.target = this; /*Github:https://github.com/answershuto*/ /*觸發(fā)渲染操作進(jìn)行依賴收集*/ this.cb.call(this.vm); } update () { this.cb.call(this.vm); } }
開始依賴收集
class Vue { constructor(options) { this._data = options.data; observer(this._data, options.render); let watcher = new Watcher(this, ); } } function defineReactive (obj, key, val, cb) { /*在閉包內(nèi)存儲(chǔ)一個(gè)Dep對象*/ const dep = new Dep(); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: ()=>{ if (Dep.target) { /*Watcher對象存在全局的Dep.target中*/ dep.addSub(Dep.target); } }, set:newVal=> { /*只有之前addSub中的函數(shù)才會(huì)觸發(fā)*/ dep.notify(); } }) } Dep.target = null;
將觀察者Watcher實(shí)例賦值給全局的Dep.target,然后觸發(fā)render操作只有被Dep.target標(biāo)記過的才會(huì)進(jìn)行依賴收集。有Dep.target的對象會(huì)將Watcher的實(shí)例push到subs中,在對象被修改觸發(fā)setter操作的時(shí)候dep會(huì)調(diào)用subs中的Watcher實(shí)例的update方法進(jìn)行渲染。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享javascript中vue響應(yīng)式原理及依賴收集的示例分析內(nèi)容對大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,遇到問題就找億速云,詳細(xì)的解決方法等著你來學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。