MVVM的实现原理入门了解2-- 数据绑定初始化

数据绑定主要应用文件为 observer.js 

因为 在MVVM文件中 数据代理后 执行的代码为

Object.keys(data).forEach(function(key) { 
    me._proxyData(key); 
});
observe(data, this);

因此 数据绑定为第二步

observer.js的内容 

//数据绑定
function Observer(data) {
    this.data = data; //将data 保存到 Observer中
    this.walk(data);  //执行walk并将data传入
}

Observer.prototype = {
    // 执行
    walk: function(data) {
        var me = this; // 使用me替换this
        Object.keys(data).forEach(function(key) { //将data中的属性名组成数组并遍历
            me.convert(key, data[key]); //调用convert方法 传入 data的属性名与属性值
        });
    },
    convert: function(key, val) {
        //定义响应式 传入Observer的data 以及 data中的属性名与属性值
        this.defineReactive(this.data, key, val); //实现数据绑定 也就是实现 defineProperty 数据劫持
    },
    // 使用数据劫持实现数据绑定的关键代码
    defineReactive: function(data, key, val) {
        //在数据劫持时 创建Dep dependency(依赖)
        // dep中的个数又 data中的属性个数确定 且与data的属性一一对应 (包括属性为对象中的属性)
        //        data:{ name:"zh",option:{ a:"a",b:"b" }} 此时dep数量为4个 分别为name  option a b 等对应
        var dep = new Dep();
        var childObj = observe(val); // 如果data中的属性为对象 那么递归调用observe 直到不是对象
        Object.defineProperty(data, key,{
            enumerable: true, // 可枚举
            configurable: false, // 可配置性为false 那么不能在进行删除、重写等操作
            get: function() {
                // 建立dep与watcher关系的代码
                if (Dep.target) {  //Dep中的target默认为null
                    dep.depend();
                }
                return val;
            },
            //setter 主要用来更新界面
            set: function(newVal) {
                if (newVal === val) {
                    return;
                }
                val = newVal;
                // 新的值是object的话,进行监听
                childObj = observe(newVal);
                // 通知订阅者 也就是通知dep中 subs中存入的watcher
                dep.notify();
            }
        });
    }
};

function observe(value, vm) {
    // 判断data 不存在或者 不为一个对象 结束执行
    if (!value || typeof value !== 'object') {
        return;
    }
    // 返回Observer实例 传入 data
    return new Observer(value);
};


var uid = 0;

function Dep() {
    this.id = uid++; //标识
    this.subs = []; //subs --> Subscribe 订阅/订阅者  创建订阅者数组(存放的watcher)
}

Dep.prototype = {
    addSub: function(sub) { //watcher的添加
        this.subs.push(sub);  //将subs数组中 添加对应的watcher
    },
    // 调用将addDep的this指向为 Dep.target 也就是说 watcher调用addDep
    // 建立dep与watcher之间关系的代码
    depend: function() {
        Dep.target.addDep(this);
    },
    removeSub: function(sub) {
        var index = this.subs.indexOf(sub);
        if (index != -1) {
            this.subs.splice(index, 1);
        }
    },
    notify: function() {
    // 遍历subs中的数据 然后为每一个sub调用update更新 因为subs中存入的是每一个watcher 因此调用每一个watcher对应的update方法
        this.subs.forEach(function(sub) {
            sub.update();
        });
    }
};

Dep.target = null;
var childObj = observe(val);

具体每一步都进行了注释 现在 解释下

在observer.js中的执行流程

  1. 在MVVM中调用observe(data,this) -->  因为data为一个对象因此 new Observer(data)
  2. 将data保存在observer函数上 调用walk并传递data
  3. walk方法地主要功能为 遍历data中的属性值与属性名 并传递给实现数据绑定的关键方法defineReactive 省略convert
  4. defineReactive的作用就是使用数据劫持实现数据绑定  defineReactive中
    1. new Dep(); 稍后讲
    2. var childObj = observe(val); 这一步的作用就是将传递的data中的属性值进行递归遍历,查看是否为对象,跳出递归的条件为data中的属性值不为对象时   如 data:{ name:"中国",city:{ north:"北京" } }     目的就是 将data中的name属性进行数据劫持、对city中的north属性进行数据劫持
    3. 使用object.defineProperty 实现数据劫持 主要就是为data中的每一个属性名添加set与get方法
      1. set方法 值发生改变时调用 
        set: function(newVal) {
            if (newVal === val) {
                return;
            }
            val = newVal;
            childObj = observe(newVal);//判断这个新的值是否为对象 如果是则进行监视
            dep.notify();//通知订阅者
        }
      2. get方法 查询这个值时调用
        get: function() {  
            if (Dep.target) {  //由于此时Dep.target = null 因此不执行该代码
                dep.depend(); 
            }
            return val;  
        }

对于Dep的理解

dep中有两个属性 一个为id数字,一个为subs数组 在observer.js文件中有一个

var uid = 0; 
function Dep() {
    this.id = uid++; 
    this.subs = [];
}

因此 每一次new Dep id都会被加1 从零开始  而 执行new Dep的代码 是在defineReactive进行 

defineReactive方法是为每一个data中的属性进行绑定set与get事件  由此可以指定 dep的id就是data中每一个属性的唯一标识

比如 

 data: {
      someStr: 'hello ',
      htmlStr: '<span style="color: #f00;">red</span>',
      child: {
           someStr: 'World !'
      }
},

那么  someStr对应的dep.id为0  htmlStr对应的dep.id为1 child对应的dep.id为2  child.someStr对应的dep.id为3

目前observer初始化所作的全部内容 由于Dep.target = null 所以dep.depend()不执行     dep.notify()因为subs数组为空 所以也不执行 

posted @ 2019-04-21 10:14  嘿!巴扎嘿  阅读(49)  评论(0)    收藏  举报