面試官不想和你說話並向你扔了一個MVVM原理


先講個方法

在咱們講MVVM的框架實現原理以前咱們要先講一個方法Object.defineProperty(),這個方法是Vue這類MVVM原理實現很重要的一個方法,由於這是一個ES5的方法,因此不兼容IE8及一下,這也就是爲何Vue不兼容IE8的緣由。git

從字面意思上來看咱們也知道,Object.defineProperty()是給對象定義屬性的一個方法,在咱們平時賦屬性常常會這樣作:github

這樣看似並無什麼問題。數組

咱們能夠對這個屬性進行修改,刪除,枚舉。。。隨意調戲bash

這樣的定義方式看似很方便,數據能夠隨意咱們處理,可是實際上它有不少不便之處,舉個🌰框架

若是像咱們剛纔那樣定義屬性,length就是會被遍歷出來,這樣會給咱們的使用形成很大麻煩,這樣的屬性是如何被定義的?這就要用到Object.definePropertyide


語法函數

Object.defineProperty(obj,prop,descriptor)複製代碼

參數

  • objspa

  • 要在其上定義屬性的對象。3d

  • prop代理

  • 要定義或修改的屬性的名稱。

  • descriptor

  • 將被定義或修改的屬性描述符。


1.description是數據屬性

屬性描述符總共有4個參數,咱們能夠傳入一個對象

  • configurable

  • 當且僅當該屬性的 configurable 爲 true 時,該屬性描述符纔可以被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false

  • enumerable

  • 當且僅當該屬性的enumerabletrue時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false

  • value

  • 該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined

  • writable

  • 當且僅當該屬性的writabletrue時,value才能被賦值運算符改變。默認爲 false


接下來寫個小🌰


2.description是訪問器屬性

這個時候description也是有4個參數

  • configurable

    當且僅當該屬性的 configurable 爲 true 時,該屬性描述符纔可以被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false

  • enumerable

    當且僅當該屬性的enumerabletrue時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false

  • get

    一個給屬性提供 getter 的方法,若是沒有 getter 則爲 undefined。該方法返回值被用做屬性值。默認爲 undefined

  • set

    一個給屬性提供 setter 的方法,若是沒有 setter 則爲 undefined。該方法將接受惟一參數,並將該參數的新值分配給該屬性。默認爲 undefined


當description屬性有get或set訪問器屬性的時候,不能有writable和value屬性,由於get和set的做用能夠替代者兩個屬性

接下來寫個小🌰

模擬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複製代碼
相關文章
相關標籤/搜索