簡單易讀的 vue 依賴收集

簡單理解Vue中的依賴收集過程

依賴收集的始末

你好! 這是一篇記載關於Vue源碼中依賴收集過程的文章。

從vue初始化開始

當我們用new操作符構建一個新的Vue實例時會發生:

  1. 初始化,將 elel 置爲空,root 始終指向根實例,根節點唯一,且爲最初聲明的Vue實例,後續的組件都是 $root 的children,源碼中 this.\$root = this.$parent.$root || this,其他的就是將屬性置爲初始值;
  2. 混入來自Vue構造函數的默認指令以及options,如 v-model,v-text ;
  3. 調用beforeCreated,此時data,computed,methods等還未初始化,故無法進行異步請求來賦值;
  4. 依次進行mixins混入,組件,props,methods,data,computed,watch,events 初始化,混入時讀取mixins內的 options ,若原始的options 內的基本類型屬性不在 mixins 內找到,則會添加,否則保留原始的數據。如果時引用類型,methods watch computed 不替換,若是生命週期,則會將mixins內的處理邏輯添加至對應鉤子的事件隊列中。對組件進行 init 操作得到一個 Vue 實例,將其添加至當前實例的 components 上。將對應的props,methods,data,computed綁定在vm上,不同的是 computed 和 watch 會創建 Watcher 實例,且computed是lazy,即如果依賴的數據未發生變化,會使用之前的緩存,而 data 會做數據劫持生成自己的唯一Dep;如果當前組件是子組件則會讀取組件標籤上的 @或者v-on指令,並會在當前實例上通過 $on 註冊事件,如果當前組建內有調用 $emit 進行觸發,則會執行註冊事件的回調。
  5. 初始化完成,調用created,此時可以通過 this 對值進行修改以及觸發事件;
  6. 進入compile階段

依賴收集

  • 觸發途徑
  • 處理過程
    所有通過 new 操作符調用 Watcher 生成新對象的行爲都會產生依賴收集,比如computed,watch,模板文本 {{obj.a}} ,v-bind,v-model 等都會產生依賴收集行爲。
    當有新的Watcher實例生成時會執行get方法,而get方法內的代碼是這樣的:
get() {
        const vm = this.vm
        // 在讀取值時先將觀察者對象賦值給Dep.target 否則Dep.target爲空 不會觸發收集依賴
        Dep.target = this
        let value =  this.getter.call(vm, vm)
        if (this.filters) {
            value = vm._applyFilters(value, this.filters)
        }
        // 觸發依賴後置爲空
        Dep.target = null
        return value
    },

首先會將Dep.target設置爲當前 Watcher 實例,同時訪問表達式,即如果監聽的是 obj.a ,則會觸發 obj.a 的 get ,由於初始化時被劫持,同時生成了專屬Dep ,

function defineReactive(obj, key, val) {
    const dep = new Dep()
    console.log(dep)
    // 遞歸監聽
    let childOb = observe(val)
    
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get() {      
            // 收集對應的觀察者對象
            if (Dep.target) {
                dep.depend()
                console.log(dep)
                if (childOb) {
                    childOb.dep.depend()
                }
                if (isArray(val)) {
                    for (let e, i = 0, l = val.length; i < l; i++) {
                        e = val[i]
                        e && e.__ob__ && e.__ob__.dep.depend()
                    }
                }
            }
            return val
        },
        set(newVal) {
            if (val === newVal) {
                return
            }
            val = newVal
            // 遞歸監聽
            childOb = observe(newVal)
            // 觸發更新
            dep.notify()
            
        }
    })
}

dep會調用depend函數,將watcher加入自己的subs,同時watcher也會將dep加入自己的deps,這樣當值發生變化時,調用dep.notify通知dep下所有subs中的watcher 執行 update 操作 ,而後就可以在wacher 的 callback 內拿到新舊數據了。

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