vue的響應式原理html
當你把一個普通的 JavaScript 對象傳入 Vue 實例做爲 data
選項,Vue 將遍歷此對象全部的屬性,並使用 Object.defineProperty
把這些屬性所有轉爲 getter/setter。vue
’Object.defineProperty
是 ES5 中一個沒法 shim 的特性,這也就是 Vue 不支持 IE8 以及更低版本瀏覽器的緣由。設計模式
這些 getter/setter 對用戶來講是不可見的,可是在內部它們讓 Vue 可以追蹤依賴,在屬性被訪問和修改時通知變動。每一個組件實例都對應一個 watcher 實例,它會在組件數組
渲染的過程當中把「接觸」過的數據屬性記錄爲依賴。以後當依賴項的 setter 觸發時,會通知 watcher,從而使它關聯的組件從新渲染。瀏覽器
經過這麼長時間的學習,我用比較直白的話來詳細的解釋一下原理ide
首先我們須要掌握兩個方法學習
一個是Object.defineProperty,一個是訂閱者設計模式ui
Object.defineProperty方法this
Object.defineProperty會直接在一個對象上定義一個新屬性或者修改一個對象的現有屬性,而且返回這個對象。spa
Object.defineProperty(obj,prop,descriptor)有三個屬性。obj是要定義屬性的對象。prop是須要定義或者修改的屬性的名稱,descrptor是被定義或修改的屬性描述符。
setter或者getter是js對象中用來設置屬性或獲取屬性的方法,他是在建立對象的時候指明的。
當data對象中有值存在時,vue就會獲取這個對象,獲取這個對象的時候就會調用get,而後會用Object.keys()方法拿到這個data對象中的每一個屬性,
而後進行遍歷,獲得每一個屬性,而後經過Object.defineProperty的set和get方法進行設置屬性值或者是獲取屬性值。
發佈-訂閱模式
舉個例子來講明一下發布者訂閱者模式。上面的例子中,就是讓張三和李四來訂閱了這個message屬性的改變。
發佈-訂閱是一種消息範式,消息的發送者(稱爲發佈者)不會將消息直接發送給特定的接收者(稱爲訂閱者)。而
是將發佈的消息分爲不一樣的類別,無需瞭解哪些訂閱者(若是有的話)可能存在。一樣的,訂閱者能夠表達對一個或多個類別的興趣,
只接收感興趣的消息,無需瞭解哪些發佈者(若是有的話)存在。
舉一個例子,你在微博上關注了A,同時其餘不少人也關注了A,那麼當A發佈動態的時候,微博就會爲大家推送這條動態。
A就是發佈者,你是訂閱者,微博就是調度中心,你和A是沒有直接的消息往來的,全是經過微博來協調的(你的關注,A的發佈動態)。
vue就是經過這種發佈者訂閱者模式來處理響應式數據的。
下面的代碼只是把整個響應式的過程解釋了一下,可是源碼並非這麼寫的,可是整個流程是這樣走滴,只不過我簡化可不少~~但願你們能明白哈
// 而後經過遍歷obj這個對象拿到這個屬性------
// Object.keys(obj)返回的是一個數組形式的這個對象的屬性 // key表明的是每一個屬性 Object.keys(obj).forEach(key => { // 拿到這個屬性 let value=obj[key] Object.defineProperty(obj,key,{
// 設置屬性 set(newValue) { // 能夠在這個位置監聽key的改變 value=newValue
// dep.notify() 而後在相似這個地方通知訂閱者發生改變
<!-- 發生改變的時候,須要獲取到誰在用這個值,假如這裏有倆我的在用張三和李四,此時就須要對每一個用到message進行解析,
根據解析html代碼,來獲取到哪些人在用這些屬性,他在獲取message裏的
值的時候,他會調用一下get方法,誰用這個message,誰都會調取一次get,到時候就能知道究竟是誰調用了這個message,
一旦newValue發生改變,就會通知這三我的,而後會通知這三我的,讓這三我的把界面更新一下 get方法---自身的update方法
這時候就須要用發佈訂閱者模式來監聽,讓這三我的訂閱這個屬性的改變-->
console.log('監聽'+key+'改變'+':'+value) }, get() {
在相似這個的地方建立一個watch的對象。來獲取每一個訂閱者
// const wat1=new Watcher('涵涵');
console.log('獲取'+key+'對應的值')
return value } }) }) // 他就會執行對象裏的set屬性,把他的名字設置成'hanhan' obj.name='hanhan'
咱們把多個訂閱者對象添加到發佈者裏面,一旦值發生改變,發佈者只要去調用了本身的notify,就會立馬通知以前全部的訂閱者,
訂閱者是一個數組,遍歷數組裏的每一個成員,數組裏面每一個成員都有watch,通知訂閱者去更新本身的update。而後進行更新界面
// 發佈者 class Dep { constructor() { // 訂閱了一個數組,用這個數組去記錄全部的訂閱者 this.subscription=[] } // 加入訂閱者,加進去這我的,也就是張三李四,這兩我的 addSub(watch) { this.subscription.push(watch) }; notify() { this.subscription.forEach(item=>{ // 調用它本身的unpate去更新 item.update() }) } } // 觀察者,用於監聽觀察,經過這個類建立對象, // 訂閱者 class Watcher { constructor(name) { this.name=name; } update() { // 把本身的內容進行更新 console.log(this.name+'發生update') } } // 實例一個dep對象 const dep=new Dep(); const wat1=new Watcher('張三'); dep.addSub(wat1) //張三就被訂閱者放到了subscription的數組裏面 const wat2=new Watcher('李四'); dep.addSub(wat1) //李四就被訂閱者放到了subscription的數組裏面 dep.notify()//這裏定義notify,那麼剛纔的兩個訂閱者,就全被我通知到了 }
下面我用一張圖對上面的內容進行一個總結吧,總結的比較膚淺,都是表面的知識,但願你們多多指點,一塊進步哈。
observer主要是對data對象中的數據進行了劫持監聽,利用Object.defineProperty,一個屬性對應一個dep對象,name有一個dep對象,age有一個dep對象,
他們是一一對應的關係,每一個dep對象裏面都 有他的觀察者對象,觀察者1,觀察者2...當屬性值發生變化的時候就會去調用dep中的notify,通知watcher,利用watcher去更新視圖。
當el傳進complie裏面時,主要是作了兩件事
一是解析html,建立對應的watcher,放到對應的observer中的dep對象中去,具體怎麼放得,上面有說哈,
二是他還會根據el中的內容初始化view,也就是解析我們的{{message}},在界面中顯示出 「嘻嘻」。
假如咱們把name中的屬性值改爲了 '哈哈 ',那麼observer中的Object.defineProperty立馬會監聽到值的改變,調用notify的方法,遍歷watcher,進行update,而後更新視圖,把 why 變爲 「哈哈」