文章目錄
- VUE2雙向綁定的原理
- 1. 什么是雙向綁定
- 2. 雙向綁定的原理
- 2.1 ViewModel的重要作用
- 2.2 雙向綁定的流程
- 3. 雙向綁定的實現
- 3.1 data響應化處理
- 3.2 Compile編譯
- 3.3 依賴收集
VUE2雙向綁定的原理
1. 什么是雙向綁定
- 講雙向綁定先講單項綁定,啥叫單項綁定,就是一句話就是通過Model去改變View,再直白點,就是通過js代碼把數據改變后,html視圖也跟著變化
- 那雙項綁定就很好理解了,在單項綁定的基礎上,如果view改變了,Model也能同步變化
- 一句話概括就是,Model變化View跟著跟新,View跟新,Model跟著變化,這就是雙向綁定
2. 雙向綁定的原理
- 其實我們可以很容易想到一點就是,如果A變化了想要B跟著變化,最簡單的方式就是,A變化的時候通知一下B就行,這就是基本思路
- 在VUE2 中,雙向綁定由三個重要部分構成
- 數據層(Model),應用的數據及業務邏輯
- 視圖層(View),應用的展示效果,理解為UI組件
- 業務邏輯層(ViewModel),框架封裝的核心,他主要負責把數據層和視圖層關聯起來,這就是MVVM模型
2.1 ViewModel的重要作用
- ViewModel主要干兩件事
- 數據變化后,更新視圖
- 視圖變化后,更新數據
- 那么問題來了,怎么通知呢,我們怎么知道數據變化后,通知哪些視圖呢
- 這要依賴ViewModel的兩個重要部件
- 監聽器(Observer),對所有數據的屬性進行監聽
- 解析器(Compiler),對元素節點的指令進行掃描跟解析,根據指令模板替換數據,以及綁定相應的更新函數
2.2 雙向綁定的流程
- 雙向綁定的流程
- 我們在new Vue()時,執行初始化,對data執行相應化處理,這個過程發生在Observer中
- 同時對模板執行編譯,找到其中動態綁定的數據,從data中獲取并初始化視圖,這個過程發生在Compiler中
- 同時定義一個更新函數和Watcher,將來對應數據變化時,Watcher會調用更新函數
- 由于data中的數據的某個key可能出現在視圖的多處,所以每個key都需要一個管家Dep來管理多個Watcher
- 將來數據一旦發生變化,會首先找到對應的Dep,通過Dep李曼的所有Watcher執行更新函數
3. 雙向綁定的實現
3.1 data響應化處理
- 我們來創建一個構造函數,執行初始化,對data數據執行響應化處理
class Vue{constrcutor(options){this.$options=options;this.$data=options.data;//對data選項做響應式處理Observe(this.$data)//代理data到vm上proxy(this)// 執行編譯new Compile(options.el,this)}
}
function Observe(obj){if(typeof obj!=='object' || obj===null){return;}new Observer(obj)
}
class Observer{constructor(value){this.value=value;this.walk(value)}walk(obj){Object.keys(obj).forEach((key)=>{defineReactive(obj,key,obj[key])//內部是Object.defineProperty實現,后面會講})}
}
3.2 Compile編譯
- 對元素節點的指令機型掃描跟解析,根據指令模板替換數據,以及綁定相應的更新函數
class Compile{constructor(el,vm){this.$vm=vm;this.$el=document.querySelector(el);//獲取DOMif(this.$el)this.compile(this.#el)}compile(el){const childNodes=el.ChildNodes;Array.from(childNodes).forEach((node)=>{//遍歷子元素if(this.isElement(node)){//判斷是否為節點/*編譯元素*/}else if(this.inInterpolation(node)){//是否為差值文本/*編譯差值文本*/}if(node.childNodes && node.childNodes.length>0){this.compile(node)}})}isElement(node){return node.nodeType===1}isInterpolation(node){return node.nodeType===3 && /\{\{(.*)\}\}/.test(node.textContent)}
}
3.3 依賴收集
-
視圖中會用到data中的某個key,這被稱為依賴,一個key可能出現在視圖中的多個位置,每次都需要收集出來用一個Watcher來維護他們,這個過程被稱為依賴收集,很多歌Watcher需要一個Dep來管理,需要更新時由Dep統一通知
-
基本思路
- defineReactive為每一個key創建一個Dep,比如data1創建Dep1
- 初始化視圖時,讀取某個key,例如data1,就創建一個watcher1
- 由于讀取key時觸發getter方法,邊疆watcher1天假到data1的Dep1中份
- 當data1更新時,觸發setter,通過Dep1通知所有的watcher更新
class Wacther{constructor(vm,key,updater){this.$vm=vm;this.$key=key;this.updaterFn=updater;//創建實例時,把當前實例指定到Dep.target靜態屬性上Dep.target=this;vm[key]//讀一下key,觸發getDep.target=nullupdate(){this.updaterFn.call(this.$vm,this.$vm[this.$key])}}
}
class Dep{constructor(){this.deps=[];//依賴管理 }addDep(dep){this.deps.push(dep)}notify(){this.deps.forEach((dep)=>{dep.update()})}
}
// 創建Watcher時觸發getter
function defineReactive(obj,key,val){this.observe(val)const dep=new Dep()Object.defineProperty(obj,key,{get (){Dep.target && dep.addDep(Dep.target)//Dep.target就是Watcher實例return val},set(newVal){if(newVal===val)returndep.notify()//通知dep執行更新方法 }})
}