MVVM的實現原理入門瞭解4-- Dep發佈者與Watcher訂閱者之間的關係

前篇講到compile.js執行進行模板解析 同時new Watcher實例化watcher

由於watcher中在初始化時 使用 this.value = this.get()方法 

get: function() {
        Dep.target = this;
        var value = this.getter.call(this.vm, this.vm);
        Dep.target = null;
        return value;
    },
  1. get方法的調用使 Dep.target = watcher(也就是data中的屬性解析模板時創建的watcher)
  2. 然後調用var value = this.getter.call(this.vm,this.vm) 進行取值
    1. 取對應data中屬性名的屬性值  由於observer對data中的每一個屬性都進行了數據劫持添加了get與set方法
    2. 由於是取值執行get方法 
       get: function() {
                      if (Dep.target) {  //Dep中的target此時爲watcher
                          dep.depend(); //執行此代碼
                      }
                      return val;
                  },
       depend: function() {
              Dep.target.addDep(this);
          },

      由於此時Dep.target 爲watcher 因此 調用watcher.addDep(this) this此時爲data屬性名對應的dep

  3. 執行addDep

    addDep: function(dep) {
            //判斷watcher與dep的關係是否建立 防止同一個data中的屬性對應的dep與對應的watcher重複建立關係
            if (!this.depIds.hasOwnProperty(dep.id)) {
                dep.addSub(this);  //調用dep的addSub方法 傳入 watcher
                this.depIds[dep.id] = dep; //讓depIds容器中的dep.id屬性名的屬性值爲 dep
                // depIds:{ dep.id:dep }  此時也就是將 dep保存到了 watcher中
            }
        },

    由於depIds存儲的是dep 而dep屬性中又有id與subs 兩個屬性 因此

    1. dep.addSub(將當前的watcher傳遞給dep 並將watcher保存到dep中的subs中)

    2. 將當前的dep保存到watcher中的depIds中去 以dep.id爲屬性名 dep爲屬性值 

    3. 由於解析表達式才創建watcher 因此每次的watcher都是不同的 因此每一次的this.depIds爲空

  4. addSub: function(sub) { //watcher的添加
            this.subs.push(sub);  //將subs數組中 添加對應的watcher
        },

    因此這樣可以將每次不同的watcher完全的保存到對應的dep.subs中

    比如 

    <p>{{someStr}}</p>
    <p v-text="someStr"></p>

    那麼此時  watcher應爲2個(模板中解析指令創建watcher)  dep爲1個(進行數據劫持時創捷dep)  data:{someStr:"hello"}  ---> 因此 depIds存入的爲   depIds:{ dep.id:dep }   --> {dep.id:{dep.id,dep.subs}} --> { 0 : {0,[watcher,watcher] } } 

  5. 初始化完畢返回val 也就是對應屬性的屬性值

    walk: function(data) {
            var me = this; 
            Object.keys(data).forEach(function(key) {
                me.convert(key, data[key]);
            });
        },
        convert: function(key, val) { 
            this.defineReactive(this.data, key, val);
        },
        defineReactive: function(data, key, val) {
            var dep = new Dep();
            var childObj = observe(val);
            Object.defineProperty(data, key,{
                enumerable: true, 
                configurable: false, 
                get: function() {
                    if (Dep.target) {
                        dep.depend();
                    }
                    return val;
                }
                set:....
    }

     

當執行更改data中屬性名對應的屬性值時 進行操作

當數據發生更改時 執行set方法

set: function(newVal) {
                if (newVal === val) {
                    return;
                }
                val = newVal;
                // 新的值是object的話,進行監聽
                childObj = observe(newVal);
                // 通知訂閱者 也就是通知dep中 subs中存入的watcher
                dep.notify();
            }

 

  1. 執行dep.notify() 
    notify: function() {
            this.subs.forEach(function(sub) {
                sub.update();
            });
        }
     對變化屬性值的對應的subs進行遍歷並執行watcher.update方法 因此對應dep的每一個watcher都將進行頁面更新
  update: function() {
        this.run();
    },
    run: function() {
        // 讓value等於 this.get
        var value = this.get(); //獲取最新的值
        var oldVal = this.value;//獲取舊值
        if (value !== oldVal) {// 如果新的值與舊的值不相等
            this.value = value; //讓value爲新的值
            // cb.call 是執行Watcher中的cb形參
            // 調用回調函數
            this.cb.call(this.vm, value, oldVal); //更新界面的回調函數
        }
    },

主要執行爲cb方法  執行watcher的回調函數 用於視圖更新

new Watcher(vm, exp, function(value, oldValue) {
            // 回調函數 用於更新data中屬性名對應的屬性值時 節點發生改變
            updaterFn && updaterFn(node, value, oldValue);
        });

 

MVVM的原理圖  前3張爲https://github.com/DMQ/mvvm作者提供

最後一張爲個人理解

完全個人理解 希望大佬多多指定

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