vue學習之深入響應式原理
- 2019 年 11 月 8 日
- 筆記
vue的響應式原理
當你把一個普通的 JavaScript 對象傳入 Vue 實例作為 data
選項,Vue 將遍歷此對象所有的屬性,並使用 Object.defineProperty
把這些屬性全部轉為 getter/setter。
’Object.defineProperty
是 ES5 中一個無法 shim 的特性,這也就是 Vue 不支持 IE8 以及更低版本瀏覽器的原因。
這些 getter/setter 對用戶來說是不可見的,但是在內部它們讓 Vue 能夠追蹤依賴,在屬性被訪問和修改時通知變更。每個組件實例都對應一個 watcher 實例,它會在組件
渲染的過程中把“接觸”過的數據屬性記錄為依賴。之後當依賴項的 setter 觸發時,會通知 watcher,從而使它關聯的組件重新渲染。
通過這麼長時間的學習,我用比較直白的話來詳細的解釋一下原理
首先咱們需要掌握兩個方法
一個是Object.defineProperty,一個是訂閱者設計模式
Object.defineProperty方法
Object.defineProperty會直接在一個對象上定義一個新屬性或者修改一個對象的現有屬性,並且返回這個對象。
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 變為 “哈哈”