2017百度前端技术学院——vue源码分析之——动态数据绑定二(发布/订阅者模式)

源码地址

一、发布/订阅者模式

订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码;如下图

二、程序详解(实现$watcher方法)

一共三个js文件:

  • index.js:用来遍历数据,并在数据对象的每个属性上添加gettersetter,当有数据变动的时候给通道发送一个notify
  • dep.js:通道,用来连接发布者和订阅者,有一个数组变量通过addSub方法来存放watcher,当通道收到notify之后遍历数组触发每个watcherupdate
  • watcher.js:充当订阅者,获取更新后的数据并且执行回调函数
watcher.js:
// Watcher的实例就是订阅者
function Watcher(vm,exp,cb){
    console.log("watcher执行了");
    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的实例调用了getter
w.get = function (){
    Dep.target = this//表明是watcher调用了getter
    return this.vm.data[this.exp] //这里会调用get方法
}
这里有两个小问题:
  1. 如何把watcher加入到数组里;
  2. 如何判断是watcher触发的getter还是普通触发getter
解决办法:

在dep.js中设置一个全局变量,如果是watcher触发的getter就把这个watcher实例赋值给这个变量,然后在index.js中的getter里进行判断,如果全局变量不为null,则把watcher实例添加到数组;

dep.js代码如下:

// 定义一个Dep(调度中心),用来维护一系列观察者,方便添加观察者
function Dep(){
    this.subs = [] //存放订阅者的数组
}
var s = Dep.prototype;
// 把订阅者都存到数组里面
s.addSub = function (sub){
    this.subs.push(sub)
}
// 订阅者想订阅的事件,注册到事件中心
s.notify = function (){
    // 一旦调用了set就触发notify,然后遍历每个观察者,并触发他们相应的update方法
    this.subs.forEach(function (sub){
        sub.update();
        /* 
            sub.update():
            调度中心统一调度订阅者注册到调度中心的处理代码
         */
    })
}
Dep.target = null;//定义一个全局变量,用来判断是否是watcher调用了getter

index.js代码如下:

// 发布者,对data做监听,提供了某个数据项变化的能力
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函数调用的时候,触发notify
    var dep = new Dep()  //发布给订阅者
    var _this = this;
    Object.defineProperty(this.data,key,{
        configurable:true,
        enumarable:true,
        get:function (){
            console.log("你访问了"+key);

            // Watcher的实例调用了getter,将watcher加入到调度中心的数组里面
            if(Dep.target){
                dep.addSub(Dep.target)
            }

            return val
        },
        set:function (newVal){
            // 如果新设置的值和原来相等则不重新赋值
            if(newVal==val){
                return 
            }
            console.log("你设置了"+key);
            console.log("新的"+key+"="+newVal);
            val = newVal
            // 如果设置的新值是一个对象,则递归它,加上set/get
            _this.observer(newVal)
            dep.notify()//发布者发布到订阅中心
        }
    })
}
// 判断属性值是否是一个对象,如果是再深度监听
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(`我的年纪变了,现在已经是:${age}岁了`)
 });
app.data.age = 100;
// console.log(app.data.name);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 这方面的文章很多,但是我感觉很多写的比较抽象,本文会通过举例更详细的解释。(此文面向的Vue新手们,如果你是个大牛...
    Ivy_2016阅读 15,366评论 8 64
  • 双向数据绑定,我们首先来看数据改变如何触发页面的刷新。首先通过Object.defineProperty( )对v...
    209bd3bc6844阅读 1,746评论 0 1
  • 数据绑定模块 最核心的三个类: Observer,Watcher,Dep observer是Vue核心中最重要的一...
    Yang152412阅读 1,083评论 0 48
  • 我们学校小花园可谓各种奇花异草,感觉这个学校最勤劳的就数园丁们了。不知道他们从哪里搞来各种种子,点缀着很少人出没的...
    摄小影阅读 217评论 3 1
  • 最近我的主人经常熬药,不是加班完成公司工作,就是回到家躺在床上,刷微信,追电视剧到凌晨。我都受不了。我都出现不舒服...
    梅韵Eva阅读 172评论 2 2