溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

javascript中vue響應(yīng)式原理及依賴收集的示例分析

發(fā)布時(shí)間:2020-12-02 11:24:31 來源:億速云 閱讀:167 作者:小新 欄目:web開發(fā)

這篇文章主要介紹了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í)!

向AI問一下細(xì)節(jié)

免責(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)容。

AI