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數組爲空 所以也不執行 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章