Vue模擬響應式原理底層程式碼實現的示例
目錄
- 1..功能:
- 2.Observer.js功能(資料劫持):
- 3.Compiler.js功能:
- 4.Dep.js功能:
- 5.Watcher.js功能:
整體分析Vue的基本結構如下圖所示:(備註:完整程式碼地址https://github.com/1512955040/MiniVue)
上圖中,為我們模擬最小vue的整體結構,首先建立一個vue型別,它負責把data中的成員注入到vue例項中,並且轉化成getter/setter,observer的作用是資料劫持,對data中的屬性進行資料監聽,如果資料發生變化會獲取到最新的值,並通知dep。Compiler的作用是解析每個元素中的指令和差值表示式並替換成相應的資料。Dep的作用是新增觀察者,當資料發生變化時通知所有的觀察者。Watcher內部有一個Update方法負責更新檢視,下面我們用程式碼的方式一一進行實現。
1.Vue.js功能:
1-1負責接收初始化的引數(選項)
1-2負責把data中的屬性注入到vue例項,轉化成getter/setter
1-3負責呼叫observer監聽data中所有屬性的變化
1-4負責呼叫Compiler解析指令/差值表示式
類圖結構如下:
如上圖所示:vue類中有三個http://www.cppcns.com屬性,分別是$options,$el,$data,這三個屬性記錄建構函式中傳過來的引數。_proxyData為vue類中的方法
所以以_開頭的成員就是私有成員,這個方法的功能是把data中的屬性轉化為getter和setter注入到vue例項中。
class Vue{ constructor(options) { //1.通過屬性儲存選項中的資料 this.$options = options || {} this.$data = options.data || {} this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el //2.把data中的成員轉化為getter和setter,注入到vue例項中 this._proxyData(this.$data) //3.呼叫observer物件,監聽資料的變化 new Observer(this.$data) //4.呼叫compiler物件,解析指令和差值表示式 new Compiler(this) } //把Vue的屬性轉化為getter和setter,注入到Vue例項中 _proxyData(data){ //遍歷data中所有屬性 Object.keys(data).forEach(key=>{ //把data的屬性注入到vue例項全域性中 Object.defineProperty(this,key,{ enumerable:true,configurable:true,get(){ return data[key] },set(newValue){ if(newValue===data[key]){ return } data[key]=newValue } }) }) } }
2.Observer.js功能(資料劫持):
2-1 負責把data選項中的屬性轉化為響應式資料
2-2 data中的某個屬性也是物件,把該屬性轉化為響應式資料
2-3 資料變化傳送通知
類圖結構如下:
如上圖所示:
walk方法的作用是遍歷data中的所有屬性,defineReactive是定義響應式資料,也就是通過呼叫defineReactive方法把屬性轉化為getter和setter。
class Observer{ constructor(data) { this.walk(data) } //walk方法遍歷data中的所有屬性 walk(data) { //1.判斷data是否物件 if(!data || typeof data !=='object'){ return } //2.遍歷data物件的所有屬性 Object.keys(data).forEach(key=>{ this.defineReactive(data,data[key]) }) } //degineReactivce方法定義響應式資料 把屬性轉化為getter和setter defineReactive(obj,val) { let that=this // 負責收集依賴,併發送通知 let dep=new Dep() //如果val傳入物件的話也給物件裡面的屬性新增getter和setter方法 this.walk(val) Object.defineProperty(obj,{ enumerable:true,get(){ // 收集依賴 Dep.target && dep.addSub(Dep.target) return val },set(newValue){ if(newValue==val){ return } val=newValue //如果給屬性重新賦值成物件,給物件裡面的屬性重新新增getter和setter方法 //比如:歷史資料vm.msg="Hello World" 修改之後vm.msg={a:'Hwllo World'} //再次呼叫此方法給vm.msg.a重新新增getter和setter方法 that.walk(newValue) //傳送通知 dep.notify() } }) } }
3.Compiler.js功能:
3-1 負責編譯http://www.cppcns.com模板,解析指令/差值表示式
3-2 負責頁面的首次渲染
3-3 當資料變化後重新渲染檢視
類圖結構如下:
如上圖所示:
el為建構函式傳過來的options.el,vm是vue的例項,下面都是vm的方法,對DOM進行操作。compile方法內部遍歷dom物件的所有節點,並且
判斷這些節點是文字節點,如果是文字節點解析差值表示式,如果是元素節點解析指令,isTextNode和isElementNode方法判斷是文字節點還
是元素節點。compileElement和compileText方法解析差值表示式和指令。isDirective這個方法判斷元素屬性是否是指令。
4.Dep.js功能:
4-1 收集依賴,新增觀察者(watcher)
4-2 通知所有觀察者
如上圖所示:
在vue的響應式機制中,使用觀察者模式來響應資料的變化,Dep的作用是收集依賴,在getter方法中收集依賴,在setter方法中通知依賴,每
一個響應式的屬性都會場景一個Dep物件,負責收集所有依賴於該屬性的地方,所有依賴於該屬性的位置都會建立一個watcher物件,所以
Dep就是收集於該屬性的watcher物件,使用setter方法去通知依賴,當屬性發生變化http://www.cppcns.com時呼叫nodify方法去傳送通知,然後呼叫watcher物件
的update方法。
類的機構如下圖:
如上圖所示:
subs是一個數組,儲存dep中所有的watcher,addSub方法新增watcher,notify方法釋出通知
class Dep{ constructor() { //儲存所有的觀察者 this.subs=[] } // 新增觀察者 addSub(sub){ if(sub && sub.update) { this.subs.push(sub) } } //傳送通知 notify(){ this.subs.forEach(sub =>{ sub.update() }) } }
5.Watcher.js功能:
5-1 當資料變化觸發依賴,dep通知所有的Watcher例項更新檢視
5-2 自身例項化的時候往dep物件中新增自己
如上圖所示:
data中的每一個屬性都要建立一個Dep物件,在收集依賴的時候把所有物件的watcher新增到dep物件的subs陣列中,在setter物件中傳送通
知,呼叫DeuSpYgZKp物件的notify方法通知所有關聯的watcher物件更新檢視。
類圖結構如下:
如上圖所示:
update物件更新檢視,cb回撥函式,指明如何更新檢視。在更新檢視的時候需要一個屬性key(data中的屬性名稱),oldvalue是key 對應的值。
class Watcher{
constructor(vm,cb) {
this.vm=vm
//data中的屬性名稱
this.key=key
//回撥函式負責更新檢視
this.cb=cb
//把watcher物件記錄到Dep類的靜態屬性target
Dep.target =this
//觸發get方法,在get方法中會呼叫addSub
http://www.cppcns.comthis.oldValue=vm[key]
Dep.target=null
}
//當資料發生變化時更新檢視
update(){
let newValue=this.vm[this.key]
if(this.oldValue === newValue){
return
}
this.cb(newValue)
}
}
下面通過這張圖作整體流程的總結:
到此這篇關於Vue模擬響應式原理底層程式碼實現的示例的文章就介紹到這了,更多相關Vue 響應式原理內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!