Vue模擬響應式原理底層代碼實現(xiàn)的示例
整體分析Vue的基本結構如下圖所示:(備注:完整代碼github地址https://github.com/1512955040/MiniVue)
上圖中,為我們模擬最小vue的整體結構,首先創(chuàng)建一個vue類型,它負責把data中的成員注入到vue實例中,并且轉化成getter/setter,observer的作用是數(shù)據(jù)劫持,對data中的屬性進行數(shù)據(jù)監(jiān)聽,如果數(shù)據(jù)發(fā)生變化會獲取到最新的值,并通知dep。Compiler的作用是解析每個元素中的指令和差值表達式并替換成相應的數(shù)據(jù)。Dep的作用是添加觀察者,當數(shù)據(jù)發(fā)生變化時通知所有的觀察者。Watcher內部有一個Update方法負責更新視圖,下面我們用代碼的方式一一進行實現(xiàn)。
1.Vue.js功能:
1-1負責接收初始化的參數(shù)(選項)
1-2負責把data中的屬性注入到vue實例,轉化成getter/setter
1-3負責調用observer監(jiān)聽data中所有屬性的變化
1-4負責調用Compiler解析指令/差值表達式
類圖結構如下:
如上圖所示:vue類中有三個屬性,分別是$options,$el,$data,這三個屬性記錄構造函數(shù)中傳過來的參數(shù)。_proxyData為vue類中的方法
所以以_開頭的成員就是私有成員,這個方法的功能是把data中的屬性轉化為getter和setter注入到vue實例中。
class Vue{ constructor(options) { //1.通過屬性保存選項中的數(shù)據(jù) 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對象,監(jiān)聽數(shù)據(jù)的變化 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功能(數(shù)據(jù)劫持):
2-1 負責把data選項中的屬性轉化為響應式數(shù)據(jù)
2-2 data中的某個屬性也是對象,把該屬性轉化為響應式數(shù)據(jù)
2-3 數(shù)據(jù)變化發(fā)送通知
類圖結構如下:
如上圖所示:
walk方法的作用是遍歷data中的所有屬性,defineReactive是定義響應式數(shù)據(jù),也就是通過調用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,key,data[key]) }) } //degineReactivce方法定義響應式數(shù)據(jù) 把屬性轉化為getter和setter defineReactive(obj,key,val) { let that=this // 負責收集依賴,并發(fā)送通知 let dep=new Dep() //如果val傳入對象的話也給對象里面的屬性添加getter和setter方法 this.walk(val) Object.defineProperty(obj,key,{ enumerable:true, configurable:true, get(){ // 收集依賴 Dep.target && dep.addSub(Dep.target) return val }, set(newValue){ if(newValue==val){ return } val=newValue //如果給屬性重新賦值成對象,給對象里面的屬性重新添加getter和setter方法 //比如:歷史數(shù)據(jù)vm.msg="Hello World" 修改之后vm.msg={a:'Hwllo World'} //再次調用此方法給vm.msg.a重新添加getter和setter方法 that.walk(newValue) //發(fā)送通知 dep.notify() } }) } }
3.Compiler.js功能:
3-1 負責編譯模板,解析指令/差值表達式
3-2 負責頁面的首次渲染
3-3 當數(shù)據(jù)變化后重新渲染視圖
類圖結構如下:
如上圖所示:
el為構造函數(shù)傳過來的options.el,vm是vue的實例,下面都是vm的方法,對DOM進行操作。compile方法內部遍歷dom對象的所有節(jié)點,并且
判斷這些節(jié)點是文本節(jié)點,如果是文本節(jié)點解析差值表達式,如果是元素節(jié)點解析指令,isTextNode和isElementNode方法判斷是文本節(jié)點還
是元素節(jié)點。compileElement和compileText方法解析差值表達式和指令。isDirective這個方法判斷元素屬性是否是指令。
4.Dep.js功能:
4-1 收集依賴,添加觀察者(watcher)
4-2 通知所有觀察者
如上圖所示:
在vue的響應式機制中,使用觀察者模式來響應數(shù)據(jù)的變化,Dep的作用是收集依賴,在getter方法中收集依賴,在setter方法中通知依賴,每
一個響應式的屬性都會場景一個Dep對象,負責收集所有依賴于該屬性的地方,所有依賴于該屬性的位置都會創(chuàng)建一個watcher對象,所以
Dep就是收集于該屬性的watcher對象,使用setter方法去通知依賴,當屬性發(fā)生變化時調用nodify方法去發(fā)送通知,然后調用watcher對象
的update方法。
類的機構如下圖:
如上圖所示:
subs是一個數(shù)組,存儲dep中所有的watcher,addSub方法添加watcher,notify方法發(fā)布通知
class Dep{ constructor() { //存儲所有的觀察者 this.subs=[] } // 添加觀察者 addSub(sub){ if(sub && sub.update) { this.subs.push(sub) } } //發(fā)送通知 notify(){ this.subs.forEach(sub =>{ sub.update() }) } }
5.Watcher.js功能:
5-1 當數(shù)據(jù)變化觸發(fā)依賴,dep通知所有的Watcher實例更新視圖
5-2 自身實例化的時候往dep對象中添加自己
如上圖所示:
data中的每一個屬性都要創(chuàng)建一個Dep對象, 在收集依賴的時候把所有對象的watcher添加到dep對象的subs數(shù)組中,在setter對象中發(fā)送通
知,調用Dep對象的notify方法通知所有關聯(lián)的watcher對象更新視圖。
類圖結構如下:
如上圖所示:
update對象更新視圖,cb回調函數(shù),指明如何更新視圖。在更新視圖的時候需要一個屬性key(data中的屬性名稱),oldvalue是key 對應的值。
class Watcher{ constructor(vm,key,cb) { this.vm=vm //data中的屬性名稱 this.key=key //回調函數(shù)負責更新視圖 this.cb=cb //把watcher對象記錄到Dep類的靜態(tài)屬性target Dep.target =this //觸發(fā)get方法,在get方法中會調用addSub this.oldValue=vm[key] Dep.target=null } //當數(shù)據(jù)發(fā)生變化時更新視圖 update(){ let newValue=this.vm[this.key] if(this.oldValue === newValue){ return } this.cb(newValue) } }
下面通過這張圖作整體流程的總結:
到此這篇關于Vue模擬響應式原理底層代碼實現(xiàn)的示例的文章就介紹到這了,更多相關Vue 響應式原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解vue-router2.0動態(tài)路由獲取參數(shù)
本篇文章主要介紹了詳解vue-router2.0動態(tài)路由獲取參數(shù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06vue-cli 腳手架基于Nightwatch的端到端測試環(huán)境的過程
這篇文章主要介紹了vue-cli 腳手架基于Nightwatch的端到端測試環(huán)境的過程,需要的朋友可以參考下2018-09-09詳解windows下vue-cli及webpack 構建網(wǎng)站(二)導入bootstrap樣式
這篇文章主要介紹了詳解windows下vue-cli及webpack 構建網(wǎng)站(二)導入bootstrap樣式,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06