成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Vue雙向綁定的實(shí)現(xiàn)原理系列(三):監(jiān)聽(tīng)器Observer和訂閱者Watcher

legendaryedu / 1883人閱讀

摘要:至此監(jiān)聽(tīng)器和訂閱者功能基本完成,后面再加上指令解析器的功能系列文章的目錄雙向綁定的實(shí)現(xiàn)原理系列一雙向綁定的實(shí)現(xiàn)原理系列二設(shè)計(jì)模式雙向綁定的實(shí)現(xiàn)原理系列三監(jiān)聽(tīng)器和訂閱者雙向綁定的實(shí)現(xiàn)原理系列四補(bǔ)充指令解析器

監(jiān)聽(tīng)器Observer和訂閱者Watcher 實(shí)現(xiàn)簡(jiǎn)單版Vue的過(guò)程,主要實(shí)現(xiàn){{}}、v-model和事件指令的功能 主要分為三個(gè)部分

github源碼

 1.數(shù)據(jù)監(jiān)聽(tīng)器Observer,能夠?qū)?shù)據(jù)對(duì)象的所有屬性進(jìn)行監(jiān)聽(tīng);
    實(shí)現(xiàn)數(shù)據(jù)的雙向綁定,首先要對(duì)數(shù)據(jù)進(jìn)行劫持監(jiān)聽(tīng),所以我們需要設(shè)置一個(gè)監(jiān)聽(tīng)器Observer,用來(lái)監(jiān)聽(tīng)所有屬性
 
 2.Watcher將數(shù)據(jù)監(jiān)聽(tīng)器和指令解析器連接起來(lái),數(shù)據(jù)的屬性變動(dòng)時(shí),執(zhí)行指令綁定的相應(yīng)回調(diào)函數(shù),
    1.如果屬性發(fā)上變化了,就需要告訴訂閱者Watcher看是否需要更新。
    
 3.指令解析器Compile,
    對(duì)每個(gè)節(jié)點(diǎn)元素進(jìn)行掃描和解析,將相關(guān)指令對(duì)應(yīng)初始化成一個(gè)訂閱者Watcher

 因?yàn)橛嗛喺呤怯泻芏鄠€(gè),所以我們需要有一個(gè)消息訂閱器Dep來(lái)專門收集這些訂閱者,然后在監(jiān)聽(tīng)器Observer和訂閱者Watcher之間進(jìn)行統(tǒng)一管理的。=

監(jiān)聽(tīng)器Observer

Observer是一個(gè)數(shù)據(jù)監(jiān)聽(tīng)器,核心是前面一直談?wù)摰腛bject.defineProperty(),
對(duì)所有屬性監(jiān)聽(tīng),利用遞歸來(lái)遍歷所有的屬性值,對(duì)其進(jìn)行Object.defineProperty()操作:
    function definReactive(data,key,val){
        observers(val);//遞歸所有子屬性
    
        Object.defineProperty(data,key,{
            enumerable:true,
            configurable:true,
            get:function(){
                console.log("屬性"+key+"執(zhí)行g(shù)et");
                return val;
            },
            set:function(newVal){
                val = newVal;
                console.log("屬性:"+key+"以及被監(jiān)聽(tīng),現(xiàn)在值為:"+newVal.toString());
            }
        })
    }
    
    function observers(data){
        if(!data || typeof data!="object"){
            return;
        }
        Object.keys(data).forEach(function(key){
            definReactive(data,key,data[key]);
        })
    }
    var library = {
        book1:{name:""},
        book2:""
    }
    observers(library);

    library.book1.name = "vue書(shū)籍";
    library.book2 = "沒(méi)有書(shū)";
    //屬性book1執(zhí)行g(shù)et
    //屬性:name以及被監(jiān)聽(tīng),現(xiàn)在值為:vue書(shū)籍
    //屬性:book2以及被監(jiān)聽(tīng),現(xiàn)在值為:沒(méi)有書(shū) 

接下來(lái)創(chuàng)建一個(gè)收集所有訂閱者的訂閱器Dep,閱器Dep主要負(fù)責(zé)收集訂閱者,然后再屬性變化的時(shí)候執(zhí)行對(duì)應(yīng)訂閱者的更新函數(shù),
再改寫(xiě)一下訂閱器Observer,創(chuàng)建一個(gè)observer.js:

    function Observe(data){
        this.data = data;
        this.walk(data);
    }
    Observe.prototype = {
        walk:function(data){
            var self = this;
            Object.keys(data).forEach(function(key) {
                self.defineReactive(data, key, data[key]);
            });
        },
        defineReactive:function(data,key,val){
            observers(val);//遞歸所有子屬性
            var dep = new Dep();

            Object.defineProperty(data,key,{
                enumerable:true,
                configurable:true,
                get:function(){
                    if(是否需要添加訂閱者){
                        dep.addSub(Watcher);//在這里添加一個(gè)訂閱者
                    }
                    console.log("屬性"+key+"執(zhí)行g(shù)et");
                    return val;
                },
                set:function(newVal){
                    if(val === newVal){
                        return;
                    }
                    val = newVal;
                    dep.notify();//如果數(shù)據(jù)變化,通知所有訂閱者
                    console.log("屬性:"+key+"以及被監(jiān)聽(tīng),現(xiàn)在值為:"+newVal.toString());
                }
            })
        }
    }
    function observers(data){
        if(!data || typeof data!="object"){
            return;
        }
        return new Observe(data);
    }
       

    /**Dep:創(chuàng)建一個(gè)可以容納訂閱者的消息訂閱器
     * **/

    function Dep(){
        this.subs = [];
    }
    Dep.prototype = {
        addSub:function(sub){//添加訂閱者
            this.subs.push(sub);
        },
        notify:function(){//通知訂閱者
            this.subs.forEach(function(sub){
                sub.update();
            })
        }
    }
    
    可以看出,訂閱器Dep,添加一個(gè)訂閱者是在Object.defineProperty()的get里面,這是為了讓W(xué)atcher初始化進(jìn)行觸發(fā),
    因此要判斷是不是需要添加訂閱者,后面解釋。在set里面,如果數(shù)據(jù)變化,就會(huì)通知所有的訂閱者,訂閱者就會(huì)去執(zhí)行對(duì)應(yīng)的更新的函數(shù)
    以上,一個(gè)完整的訂閱器完成。

訂閱者Watcher

Watcher在初始化的時(shí)候要將自己添加進(jìn)訂閱者Dep中,如何做到:

已經(jīng)知道監(jiān)聽(tīng)器Observer是在get函數(shù)執(zhí)行了添加訂閱者Wather的操作的,

所以我們只要在訂閱者Watcher初始化的時(shí)候觸發(fā)對(duì)應(yīng)的get函數(shù),去執(zhí)行添加訂閱者操作即可,

那要如何觸發(fā)get的函數(shù):

只要獲取對(duì)應(yīng)的屬性值就可以觸發(fā)了,核心原因就是因?yàn)槲覀兪褂昧薕bject.defineProperty()進(jìn)行數(shù)據(jù)監(jiān)聽(tīng)。

注意:

我們只要在訂閱者Watcher初始化的時(shí)候才需要添加訂閱者,所以需要做一個(gè)判斷操作,

因此可以在訂閱器上做一下手腳:在Dep.target上緩存下訂閱者,添加成功后再將其去掉就可以了。

創(chuàng)建一個(gè)watcher.js

    function Watcher(vm,exp,cb){
        this.cb = cb;
        this.vm = vm;
        this.exp = exp;
        this.value = this.get();//將自己添加到訂閱器的操作
    }
    Watcher.prototype = {
        update:function () {
            this.run();
        },
        run:function () {
            var value = this.vm.data[this.exp];
            var oldVal = this.value;
            if(value != oldVal){
                this.value = value;
                this.cb.call(this.vm,value,oldVal);
            }
        },
        get:function () {
            Dep.target = this;//緩存自己
            var value = this.vm.data[this.exp];//強(qiáng)制執(zhí)行監(jiān)聽(tīng)器observer里的Object.defineProperty()里的get函數(shù)
            Dep.target = null;//釋放自己
            return value;
        }
    }

再調(diào)整下observer.js的defineReactive函數(shù)里的get操作:

    defineReactive:function(data,key,val){
        observers(val);//遞歸所有子屬性
        var dep = new Dep();

        Object.defineProperty(data,key,{
            enumerable:true,
            configurable:true,
            get:function(){
                if(Dep.target){
                    dep.addSub(Dep.target);//在這里添加一個(gè)訂閱者
                }
                console.log("屬性"+key+"執(zhí)行g(shù)et");
                return val;
            },
            set:function(newVal){
                if(val === newVal){
                    return;
                }
                val = newVal;
                dep.notify();//如果數(shù)據(jù)變化,通知所有訂閱者
                console.log("屬性:"+key+"以及被監(jiān)聽(tīng),現(xiàn)在值為:"+newVal.toString());
            }
        })
    }
    //Dep加個(gè)target屬性
    function Dep(){
        this.subs = [];
        this.target = null;
    }

簡(jiǎn)單版的Watcher設(shè)計(jì)好了,
只要將Observer和Watcher關(guān)聯(lián)起來(lái),就可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的雙向綁定數(shù)據(jù)了。
這里沒(méi)有還沒(méi)有設(shè)計(jì)解析器Compile,所以對(duì)于模板數(shù)據(jù)我們都進(jìn)行寫(xiě)死處理:
模板有個(gè)節(jié)點(diǎn),id為name,雙向數(shù)據(jù)綁定的變量name,這里大框號(hào)暫時(shí)沒(méi)有用:

 
    

{{name}}

selVue.js 關(guān)聯(lián)Observer和Watcher
    function SelfVue(data,el,exp){
        this.data = data;
        observers(data);
        el.innerHTML = this.data[exp];//初始化模板數(shù)據(jù)的值
        
        new Watcher(this,exp,function(value){
            el.innerHTML = value;
        });
        return this;
    }

頁(yè)面上實(shí)現(xiàn)雙向數(shù)據(jù)綁定:

{{name}}

打開(kāi)頁(yè)面,可以看到頁(yè)面剛開(kāi)始顯示了是“第一次顯示數(shù)據(jù)”,過(guò)了2s后就變成“重新賦值了”了。到這里,完成了一部分

注意:賦值的時(shí)候是 self_Vue.data.name = "重新賦值了",但是希望是 self_Vue.name = "重新賦值了",
需要在new SelVue的時(shí)候做個(gè)代理,讓訪問(wèn)self_Vue的屬性代理為訪問(wèn)self_Vue.data的屬性,
實(shí)現(xiàn)原理還是使用Object.defineProperty()對(duì)屬性再包一層,

修改selVue.js:

    function SelfVue(data,el,exp){
        var self = this;
        this.data = data;
    
        Object.keys(data).forEach(function (key) {
            self.proxyKeys(key);//綁定代理屬性
        });
        
        observers(data);
        el.innerHTML = this.data[exp];//初始化模板數(shù)據(jù)的值
        new Watcher(this,exp,function(value){
            el.innerHTML = value;
        });
        return this;
    }
    
    SelfVue.prototype = {
        proxyKeys:function(key){
            var self = this;
            Object.defineProperty(this,key,{
                enumerable:false,
                configurable:true,
                get:function proxyGetter(){
                    return self.data[key];
                },
                set:function proxySetter(newVal){
                    self.data[key] = newVal;
                }
            })
        }
    }

    //這下我們就可以直接通過(guò)self_Vue.name = "重新賦值了"的形式來(lái)進(jìn)行改變模板數(shù)據(jù)。

至此監(jiān)聽(tīng)器Observer和訂閱者Watcher功能基本完成,后面再加上指令解析器compile的功能!

系列文章的目錄:

Vue雙向綁定的實(shí)現(xiàn)原理系列(一):Object.defineproperty
Vue雙向綁定的實(shí)現(xiàn)原理系列(二):設(shè)計(jì)模式
Vue雙向綁定的實(shí)現(xiàn)原理系列(三):監(jiān)聽(tīng)器Observer和訂閱者Watcher
Vue雙向綁定的實(shí)現(xiàn)原理系列(四):補(bǔ)充指令解析器compile

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/51818.html

相關(guān)文章

  • Vue雙向綁定實(shí)現(xiàn)原理系列):監(jiān)聽(tīng)器Observer閱者Watcher

    摘要:至此監(jiān)聽(tīng)器和訂閱者功能基本完成,后面再加上指令解析器的功能系列文章的目錄雙向綁定的實(shí)現(xiàn)原理系列一雙向綁定的實(shí)現(xiàn)原理系列二設(shè)計(jì)模式雙向綁定的實(shí)現(xiàn)原理系列三監(jiān)聽(tīng)器和訂閱者雙向綁定的實(shí)現(xiàn)原理系列四補(bǔ)充指令解析器 監(jiān)聽(tīng)器Observer和訂閱者Watcher 實(shí)現(xiàn)簡(jiǎn)單版Vue的過(guò)程,主要實(shí)現(xiàn){{}}、v-model和事件指令的功能 主要分為三個(gè)部分 github源碼 1.數(shù)據(jù)監(jiān)聽(tīng)器Obser...

    widuu 評(píng)論0 收藏0
  • Vue雙向綁定原理,教你一步一步實(shí)現(xiàn)雙向綁定

    摘要:儲(chǔ)存訂閱器因?yàn)閷傩员槐O(jiān)聽(tīng),這一步會(huì)執(zhí)行監(jiān)聽(tīng)器里的方法這一步我們把也給弄了出來(lái),到這一步我們已經(jīng)實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的雙向綁定了,我們可以嘗試把兩者結(jié)合起來(lái)看下效果。總結(jié)本文主要是對(duì)雙向綁定原理的學(xué)習(xí)與實(shí)現(xiàn)。 當(dāng)今前端天下以 Angular、React、vue 三足鼎立的局面,你不選擇一個(gè)陣營(yíng)基本上無(wú)法立足于前端,甚至是兩個(gè)或者三個(gè)陣營(yíng)都要選擇,大勢(shì)所趨。 所以我們要時(shí)刻保持好奇心,擁抱變化,...

    Labradors 評(píng)論0 收藏0
  • 160行代碼仿Vue實(shí)現(xiàn)極簡(jiǎn)雙向綁定[詳細(xì)注釋]

    摘要:兼容性更詳細(xì)的可以看一下實(shí)現(xiàn)思路系列的雙向綁定,關(guān)鍵步驟實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(tīng)器,用重寫(xiě)數(shù)據(jù)的,值更新就在中通知訂閱者更新數(shù)據(jù)。 showImg(https://segmentfault.com/img/remote/1460000015375220?w=640&h=426); 前言 現(xiàn)在的前端面試不管你用的什么框架,總會(huì)問(wèn)你這個(gè)框架的雙向綁定機(jī)制,有的甚至要求你現(xiàn)場(chǎng)實(shí)現(xiàn)一個(gè)雙向綁定出來(lái),那對(duì)于...

    endiat 評(píng)論0 收藏0
  • Vue雙向綁定實(shí)現(xiàn)原理系列(四):補(bǔ)充指令解析器compile

    摘要:補(bǔ)充指令解析器源碼補(bǔ)充下節(jié)點(diǎn)類型的知識(shí)元素節(jié)點(diǎn)屬性節(jié)點(diǎn)文本節(jié)點(diǎn)節(jié)點(diǎn)實(shí)體引用名稱節(jié)點(diǎn)實(shí)體名稱節(jié)點(diǎn)處理指令節(jié)點(diǎn)注釋節(jié)點(diǎn)文檔節(jié)點(diǎn)文檔類型節(jié)點(diǎn)文檔片段節(jié)點(diǎn)聲明節(jié)點(diǎn)指令解析器解析節(jié)點(diǎn),直接固定某個(gè)節(jié)點(diǎn)進(jìn)行替換數(shù)據(jù)的解析模板指令,替換模板數(shù)據(jù)初始化試圖 補(bǔ)充指令解析器compile github源碼 補(bǔ)充下HTML節(jié)點(diǎn)類型的知識(shí): 元素節(jié)點(diǎn)   Node.ELEMEN...

    cheukyin 評(píng)論0 收藏0
  • Vue雙向綁定實(shí)現(xiàn)原理系列(四):補(bǔ)充指令解析器compile

    摘要:補(bǔ)充指令解析器源碼補(bǔ)充下節(jié)點(diǎn)類型的知識(shí)元素節(jié)點(diǎn)屬性節(jié)點(diǎn)文本節(jié)點(diǎn)節(jié)點(diǎn)實(shí)體引用名稱節(jié)點(diǎn)實(shí)體名稱節(jié)點(diǎn)處理指令節(jié)點(diǎn)注釋節(jié)點(diǎn)文檔節(jié)點(diǎn)文檔類型節(jié)點(diǎn)文檔片段節(jié)點(diǎn)聲明節(jié)點(diǎn)指令解析器解析節(jié)點(diǎn),直接固定某個(gè)節(jié)點(diǎn)進(jìn)行替換數(shù)據(jù)的解析模板指令,替換模板數(shù)據(jù)初始化試圖 補(bǔ)充指令解析器compile github源碼 補(bǔ)充下HTML節(jié)點(diǎn)類型的知識(shí): 元素節(jié)點(diǎn)   Node.ELEMEN...

    Kylin_Mountain 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<