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就是通過這種發佈者訂閱者模式來處理響應式數據的。

 

下面的代碼只是把整個響應式的過程解釋了一下,但是源碼並不是這麼寫的,但是整個流程是這樣走滴,只不過我簡化可很多~~希望大家能明白哈

  // 他們全用了message
  <div>{{message}}</div>
    <p>{{message}}</p>
    <i>{{message}}</i>
 
   data() {
      return {
      message:’嘻嘻’,
        name:’why’

    }

 
    },       
 
      // data傳進了new Vue裏面。內部拿到了data’中的對象,嗯,就拿這個名字命名為obj把,        
       var obj={           
        message:‘哈哈’,     
          name:‘why’          };

// 然後通過遍歷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 變為 “哈哈”