在咱們講MVVM的框架實現原理以前咱們要先講一個方法Object.defineProperty(),這個方法是Vue這類MVVM原理實現很重要的一個方法,由於這是一個ES5的方法,因此不兼容IE8及一下,這也就是爲何Vue不兼容IE8的緣由。git
從字面意思上來看咱們也知道,Object.defineProperty()是給對象定義屬性的一個方法,在咱們平時賦屬性常常會這樣作:github
這樣看似並無什麼問題。數組
咱們能夠對這個屬性進行修改,刪除,枚舉。。。隨意調戲bash
這樣的定義方式看似很方便,數據能夠隨意咱們處理,可是實際上它有不少不便之處,舉個🌰框架
若是像咱們剛纔那樣定義屬性,length就是會被遍歷出來,這樣會給咱們的使用形成很大麻煩,這樣的屬性是如何被定義的?這就要用到Object.definePropertyide
語法函數
Object.defineProperty(obj,prop,descriptor)複製代碼
obj
spa
要在其上定義屬性的對象。3d
prop
代理
要定義或修改的屬性的名稱。
descriptor
將被定義或修改的屬性描述符。
屬性描述符總共有4個參數,咱們能夠傳入一個對象
configurable
當且僅當該屬性的 configurable 爲 true 時,該屬性描述符
纔可以被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false。
enumerable
當且僅當該屬性的enumerable
爲true
時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false。
value
該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined
。
writable
當且僅當該屬性的writable
爲true
時,value
才能被賦值運算符改變。默認爲 false。
接下來寫個小🌰
這個時候description也是有4個參數
configurable
當且僅當該屬性的 configurable 爲 true 時,該屬性描述符
纔可以被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false。
enumerable
當且僅當該屬性的enumerable
爲true
時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false。
get
一個給屬性提供 getter 的方法,若是沒有 getter 則爲 undefined
。該方法返回值被用做屬性值。默認爲 undefined
。
set
一個給屬性提供 setter 的方法,若是沒有 setter 則爲 undefined
。該方法將接受惟一參數,並將該參數的新值分配給該屬性。默認爲 undefined
。
接下來寫個小🌰
模擬Array.length屬性調用的時候計算對象長度
先看一下Vue的樣子,關於Vue的使用大概就是這樣嬸兒的:
實現的最終結果就是這樣的:
接下來咱們將一一講解這些功能的實現
咱們將寫一個新的myMVVM.js的框架,來實現這些功能
固然如今仍是這樣的
數據劫持Observe
所謂數據劫持就是將咱們給對象內的數據(data)定義的屬性從新用Object.defineProperty定義一遍
咱們成功給my對象綁定上了屬性和get/set訪問器
固然,若是咱們的data數據不止一層,只調用一次的話,深層的數據就不會被數據劫持,因此咱們就須要遞歸調用,循環賦值,這也就是爲何observer()和Observer()分開寫的緣由
到這裏尚未完
好比咱們爲name賦值,會走get方法,可是若是咱們改變name的值爲一個對象,name就會變爲這個對象,同時get和set並不存在,因此咱們必須也要爲新改變的數據進行數據劫持
在咱們平時Vue的使用習慣中,調取數據基本不會用到Vue.$data.name,而是直接使用Vue.name,這樣咱們就須要用Vue來代理Vue.$data,咱們要把Vue.$data的數據從新綁定在Vue上
在Vue的官方文檔中:
當這些數據改變時,視圖會進行重渲染。值得注意的是只有當實例被建立時 data
中存在的屬性纔是響應式的。也就是說若是你添加一個新的屬性,好比:
vm.b = 'hi'
複製代碼 |
那麼對 b
的改動將不會觸發任何視圖的更新。若是你知道你會在晚些時候須要一個屬性,可是一開始它爲空或不存在,那麼你僅須要設置一些初始值。好比:
data: {
newTodoText: '',
visitCount: 0,
hideCompletedTodos: false,
todos: [],
error: null
}複製代碼 |
Vue中不能新增數據,由於新增的數據沒有get和set方法,也就沒法實現數據劫持,不能監控數據的變化
在咱們將全部的數據都綁定到實例化對象以後就要開始模板編譯,將{{data}}中的數據都替換爲真實的數據
因此咱們須要一個編譯函數Complie
在咱們將全部數據劫持以後調用模板編譯
頁面上就實現了數據替換的效果:
這個時候咱們能夠看到數據被正確編譯,可是當咱們更改這個數據的時候,實例上的數據改變了,可是模板上的數據並無跟着更新,因此接下來咱們要實現數據的刷新
看一下發布訂閱模式的原理
訂閱就是將想要監控的函數push進數組,發佈就是notify讓數組內對象的方法執行
發佈訂閱模式在咱們更新視圖時候的應用,當爲數據賦值時,將調用notify方法,讓被監控的對象執行,而被監控的應該就是Compile內的模板編譯的方法,因此咱們要將它Watcher一下
同時咱們要把剛剛粗糙的Watcher構造函數改寫一下
當數據更改時,咱們在set中調用notify
這樣就能夠實現改變數據時,視圖隨之更新
在Vue中,最大的一個特色就是雙向數據綁定,更改對象數據後,視圖會隨之跟新,一樣的若是視圖上的數據改變,對象上的數據也會隨之更新,而且視圖上其它用到此數據的部分也會更新
接下來咱們將實現這樣的雙向數據綁定的功能,首先咱們要先將name的數據賦值到input的value中,這一樣要用到咱們的模板編譯的方法,以前編譯{{}}的時候斷定的文本節點,而v-model須要斷定的是元素節點
一樣對象數據改變的時候咱們要更改input中的值,因此咱們也要在這裏訂閱一下
接下來咱們要實現當數據框輸入的時候,對象的數據也要隨之更新,這裏咱們要監聽input輸入事件
這樣咱們就實現了雙向數據綁定
就寫到這吧,寫太多了,實在沒有力氣了,隨着。。。世界索然無味(下附源碼)
這裏附上源碼,仍是但願你們能本身敲一遍代碼,清晰瞭解整個框架從無到有所經歷的每個階段,同時但願這篇文章對你們能有所幫助
https://github.com/q869939686/myProject.git複製代碼