2017百度前端技術(shù)學(xué)院——vue源碼分析之——動態(tài)數(shù)據(jù)綁定二(發(fā)布/訂閱者模式)

源碼地址

一膳汪、發(fā)布/訂閱者模式

訂閱者把自己想訂閱的事件注冊到調(diào)度中心,當(dāng)該事件觸發(fā)時候九秀,發(fā)布者發(fā)布該事件到調(diào)度中心(順帶上下文)遗嗽,由調(diào)度中心統(tǒng)一調(diào)度訂閱者注冊到調(diào)度中心的處理代碼;如下圖

二、程序詳解(實現(xiàn)$watcher方法)

一共三個js文件:

  • index.js:用來遍歷數(shù)據(jù)鼓蜒,并在數(shù)據(jù)對象的每個屬性上添加gettersetter,當(dāng)有數(shù)據(jù)變動的時候給通道發(fā)送一個notify
  • dep.js:通道痹换,用來連接發(fā)布者和訂閱者,有一個數(shù)組變量通過addSub方法來存放watcher都弹,當(dāng)通道收到notify之后遍歷數(shù)組觸發(fā)每個watcherupdate
  • watcher.js:充當(dāng)訂閱者娇豫,獲取更新后的數(shù)據(jù)并且執(zhí)行回調(diào)函數(shù)
watcher.js:
// Watcher的實例就是訂閱者
function Watcher(vm,exp,cb){
    console.log("watcher執(zhí)行了");
    this.cb = cb;
    this.vm = vm;
    this.exp = exp
    this.value = this.get()//更新前的值
}
var w = Watcher.prototype;

// 訂閱者的更新方法
w.update = function (){
    var value = this.get() //這里是更新后的值
    if(value!==this.value){
        this.value = value //用新值覆蓋舊值
        this.cb.call(this.vm,value)
    }
}
// 通過Watcher的實例調(diào)用了getter
w.get = function (){
    Dep.target = this//表明是watcher調(diào)用了getter
    return this.vm.data[this.exp] //這里會調(diào)用get方法
}
這里有兩個小問題:
  1. 如何把watcher加入到數(shù)組里;
  2. 如何判斷是watcher觸發(fā)的getter還是普通觸發(fā)getter
解決辦法:

在dep.js中設(shè)置一個全局變量畅厢,如果是watcher觸發(fā)的getter就把這個watcher實例賦值給這個變量冯痢,然后在index.js中的getter里進(jìn)行判斷,如果全局變量不為null,則把watcher實例添加到數(shù)組框杜;

dep.js代碼如下:

// 定義一個Dep(調(diào)度中心)浦楣,用來維護(hù)一系列觀察者,方便添加觀察者
function Dep(){
    this.subs = [] //存放訂閱者的數(shù)組
}
var s = Dep.prototype;
// 把訂閱者都存到數(shù)組里面
s.addSub = function (sub){
    this.subs.push(sub)
}
// 訂閱者想訂閱的事件咪辱,注冊到事件中心
s.notify = function (){
    // 一旦調(diào)用了set就觸發(fā)notify,然后遍歷每個觀察者振劳,并觸發(fā)他們相應(yīng)的update方法
    this.subs.forEach(function (sub){
        sub.update();
        /* 
            sub.update():
            調(diào)度中心統(tǒng)一調(diào)度訂閱者注冊到調(diào)度中心的處理代碼
         */
    })
}
Dep.target = null;//定義一個全局變量,用來判斷是否是watcher調(diào)用了getter

index.js代碼如下:

// 發(fā)布者油狂,對data做監(jiān)聽历恐,提供了某個數(shù)據(jù)項變化的能力
function Observer(data){
    this.data = data;
    this.walk(data)
}
// 將原型賦值給一個變量
var p = Observer.prototype;

// 遍歷對象所有屬性寸癌,包括子屬性
p.walk = function (obj){
    var _this = this;
    Object.keys(obj).forEach(function (key){
        _this.observer(obj[key])
        _this.convert(key,obj[key])
    })
}

// 綁定getter 和setter
p.convert = function (key,val){
    //每次set函數(shù)調(diào)用的時候,觸發(fā)notify
    var dep = new Dep()  //發(fā)布給訂閱者
    var _this = this;
    Object.defineProperty(this.data,key,{
        configurable:true,
        enumarable:true,
        get:function (){
            console.log("你訪問了"+key);

            // Watcher的實例調(diào)用了getter弱贼,將watcher加入到調(diào)度中心的數(shù)組里面
            if(Dep.target){
                dep.addSub(Dep.target)
            }

            return val
        },
        set:function (newVal){
            // 如果新設(shè)置的值和原來相等則不重新賦值
            if(newVal==val){
                return 
            }
            console.log("你設(shè)置了"+key);
            console.log("新的"+key+"="+newVal);
            val = newVal
            // 如果設(shè)置的新值是一個對象蒸苇,則遞歸它,加上set/get
            _this.observer(newVal)
            dep.notify()//發(fā)布者發(fā)布到訂閱中心
        }
    })
}
// 判斷屬性值是否是一個對象,如果是再深度監(jiān)聽
p.observer = function (val){
    if(typeof val ==="object"){
         new Observer(val)
    }
}
// 定義一個watcher
p.$watcher = function (exp,cb){
    new Watcher(this,exp,cb)
}

let data = {
    name:'dailu',
    age:25
};
var app = new Observer(data);
 app.$watcher('age', function(age) {
         console.log(`我的年紀(jì)變了哮洽,現(xiàn)在已經(jīng)是:${age}歲了`)
 });
app.data.age = 100;
// console.log(app.data.name);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末填渠,一起剝皮案震驚了整個濱河市弦聂,隨后出現(xiàn)的幾起案子鸟辅,更是在濱河造成了極大的恐慌,老刑警劉巖莺葫,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匪凉,死亡現(xiàn)場離奇詭異,居然都是意外死亡捺檬,警方通過查閱死者的電腦和手機再层,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堡纬,“玉大人聂受,你說我怎么就攤上這事】靖洌” “怎么了蛋济?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長炮叶。 經(jīng)常有香客問我碗旅,道長,這世上最難降的妖魔是什么镜悉? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任祟辟,我火速辦了婚禮,結(jié)果婚禮上侣肄,老公的妹妹穿的比我還像新娘旧困。我一直安慰自己,他們只是感情好稼锅,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布叮喳。 她就那樣靜靜地躺著,像睡著了一般缰贝。 火紅的嫁衣襯著肌膚如雪馍悟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天剩晴,我揣著相機與錄音锣咒,去河邊找鬼侵状。 笑死,一個胖子當(dāng)著我的面吹牛毅整,可吹牛的內(nèi)容都是我干的趣兄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼悼嫉,長吁一口氣:“原來是場噩夢啊……” “哼艇潭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起戏蔑,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蹋凝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后总棵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鳍寂,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年情龄,在試婚紗的時候發(fā)現(xiàn)自己被綠了迄汛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡骤视,死狀恐怖鞍爱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情专酗,我是刑警寧澤睹逃,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站笼裳,受9級特大地震影響唯卖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜躬柬,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一拜轨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧允青,春花似錦橄碾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至琼掠,卻和暖如春拒垃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓷蛙。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工悼瓮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留戈毒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓横堡,卻偏偏與公主長得像埋市,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子命贴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 這方面的文章很多道宅,但是我感覺很多寫的比較抽象,本文會通過舉例更詳細(xì)的解釋胸蛛。(此文面向的Vue新手們污茵,如果你是個大牛...
    Ivy_2016閱讀 15,393評論 8 64
  • 雙向數(shù)據(jù)綁定,我們首先來看數(shù)據(jù)改變?nèi)绾斡|發(fā)頁面的刷新胚泌。首先通過Object.defineProperty( )對v...
    209bd3bc6844閱讀 1,756評論 0 1
  • 數(shù)據(jù)綁定模塊 最核心的三個類: Observer省咨,Watcher肃弟,Dep observer是Vue核心中最重要的一...
    Yang152412閱讀 1,093評論 0 48
  • 我們學(xué)校小花園可謂各種奇花異草玷室,感覺這個學(xué)校最勤勞的就數(shù)園丁們了。不知道他們從哪里搞來各種種子笤受,點綴著很少人出沒的...
    攝小影閱讀 224評論 3 1
  • 最近我的主人經(jīng)常熬藥穷缤,不是加班完成公司工作,就是回到家躺在床上箩兽,刷微信津肛,追電視劇到凌晨。我都受不了汗贫。我都出現(xiàn)不舒服...
    梅韻Eva閱讀 181評論 2 2