(二)Vue-模擬實(shí)現(xiàn)響應(yīng)式

數(shù)據(jù)驅(qū)動(dòng)

  • 數(shù)據(jù)響應(yīng)式、雙向綁定、數(shù)據(jù)驅(qū)動(dòng)
  • 數(shù)據(jù)響應(yīng)式
    數(shù)據(jù)模型僅僅是普通的 JavaScript 對象,而當(dāng)我們修改數(shù)據(jù)時(shí),視圖會(huì)進(jìn)行更新,避免了繁瑣的 DOM 操作,提高開發(fā)效率
  • 雙向綁定
    數(shù)據(jù)改變,視圖改變;視圖改變,數(shù)據(jù)也隨之改變
    我們可以使用 v-model 在表單元素上創(chuàng)建雙向數(shù)據(jù)綁定
  • 數(shù)據(jù)驅(qū)動(dòng)是 Vue 最獨(dú)特的特性之一
    開發(fā)過程中僅需要關(guān)注數(shù)據(jù)本身,不需要關(guān)心數(shù)據(jù)是如何渲染到視圖

數(shù)據(jù)響應(yīng)式的核心原理

Vue 2.x

  • Object.defineProperty
  • 瀏覽器兼容 IE8 以上(不兼容 IE8)
// 模擬 Vue 中的 data 選項(xiàng) 
let data = { msg: 'hello' }
// 模擬 Vue 的實(shí)例 
let vm = {} 
// 數(shù)據(jù)劫持:當(dāng)訪問或者設(shè)置 vm 中的成員的時(shí)候,做一些干預(yù)操作
Object.defineProperty(vm, 'msg', { 
  // 可枚舉(可遍歷) 
  enumerable: true, 
  // 可配置(可以使用 delete 刪除,可以通過 defineProperty 重新定義)     
  configurable: true,
  // 當(dāng)獲取值的時(shí)候執(zhí)行 
  get () { 
      console.log('get: ', data.msg) 
      return data.msg 
   },
   // 當(dāng)設(shè)置值的時(shí)候執(zhí)行 
   set (newValue) { 
      console.log('set: ', newValue) 
      if (newValue === data.msg) { 
         return 
      }
      data.msg = newValue 
      // 數(shù)據(jù)更改,更新 DOM 的值 
      document.querySelector('#app').textContent = data.msg    
    } 
})

// 測試 vm.msg = 'Hello World' 
console.log(vm.msg)

Vue 3.x

  • Proxy
  • 直接監(jiān)聽對象,而非屬性。
  • ES 6中新增,IE 不支持,性能由瀏覽器優(yōu)化
// 模擬 Vue 中的 data 選項(xiàng) 
let data = { msg: 'hello', count: 0 }

// 模擬 Vue 實(shí)例 
let vm = new Proxy(data, { 
    // 當(dāng)訪問 vm 的成員會(huì)執(zhí)行 
    get (target, key) { 
        console.log('get, key: ', key, target[key]) 
        return target[key] 
    },
    // 當(dāng)設(shè)置 vm 的成員會(huì)執(zhí)行 
    set (target, key, newValue) { 
        console.log('set, key: ', key, newValue) 
        if (target[key] === newValue) { 
            return 
        }
        target[key] = newValue 
        document.querySelector('#app').textContent = target[key] 
    } 
})

// 測試 
vm.msg = 'Hello World' 
console.log(vm.msg)

發(fā)布訂閱模式和觀察者模式

發(fā)布/訂閱模式

  • 訂閱者
  • 發(fā)布者
  • 信號(hào)中心
    我們假定,存在一個(gè)"信號(hào)中心",某個(gè)任務(wù)執(zhí)行完成,就向信號(hào)中心"發(fā)布"(publish)一個(gè)信號(hào),其他任務(wù)可以向信號(hào)中心"訂閱"(subscribe)這個(gè)信號(hào),從而知道什么時(shí)候自己可以開始執(zhí)行。這就叫做"發(fā)布/訂閱模式"(publish-subscribe pattern)

觀察者模式

  • 觀察者(訂閱者) -- Watcher
    update():當(dāng)事件發(fā)生時(shí),具體要做的事情
  • 目標(biāo)(發(fā)布者) -- Dep
    subs 數(shù)組:存儲(chǔ)所有的觀察者
    addSub():添加觀察者
    notify():當(dāng)事件發(fā)生,調(diào)用所有觀察者的 update() 方法
  • 沒有事件中心

總結(jié):
觀察者模式是由具體目標(biāo)調(diào)度,比如當(dāng)事件觸發(fā),Dep 就會(huì)去調(diào)用觀察者的方法,所以觀察者模式的訂閱者與發(fā)布者之間是存在依賴的。
發(fā)布/訂閱模式由統(tǒng)一調(diào)度中心調(diào)用,因此發(fā)布者和訂閱者不需要知道對方的存在。

Vue 響應(yīng)式原理模擬

  1. 整體分析
  • Vue
    把 data 中的成員注入到 Vue 實(shí)例,并且把 data 中的成員轉(zhuǎn)成 getter/setter
  • Observer
    能夠?qū)?shù)據(jù)對象的所有屬性進(jìn)行監(jiān)聽,如有變動(dòng)可拿到最新值并通知 Dep
  • Compiler
    解析每個(gè)元素中的指令/插值表達(dá)式,并替換成相應(yīng)的數(shù)據(jù)
  • Dep
    添加觀察者(watcher),當(dāng)數(shù)據(jù)變化通知所有觀察者
  • Watcher
    數(shù)據(jù)變化更新視圖


  1. Vue
    負(fù)責(zé)接收初始化的參數(shù)(選項(xiàng))
    負(fù)責(zé)把 data 中的屬性注入到 Vue 實(shí)例,轉(zhuǎn)換成 getter/setter
    負(fù)責(zé)調(diào)用 observer 監(jiān)聽 data 中所有屬性的變化
    負(fù)責(zé)調(diào)用 compiler 解析指令/插值表達(dá)式

  2. Observer
    負(fù)責(zé)把 data 選項(xiàng)中的屬性轉(zhuǎn)換成響應(yīng)式數(shù)據(jù)
    data 中的某個(gè)屬性也是對象,把該屬性轉(zhuǎn)換成響應(yīng)式數(shù)據(jù)
    數(shù)據(jù)變化發(fā)送通知

  3. Compiler
    負(fù)責(zé)編譯模板,解析指令/插值表達(dá)式
    負(fù)責(zé)頁面的首次渲染
    當(dāng)數(shù)據(jù)變化后重新渲染視圖

  4. Dep(Dependency)
    收集依賴,添加觀察者(watcher)
    通知所有觀察者


    image.png
  5. Watcher
    當(dāng)數(shù)據(jù)變化觸發(fā)依賴,dep 通知所有的 Watcher 實(shí)例更新視圖
    自身實(shí)例化的時(shí)候往 dep 對象中添加自己


7.總結(jié)


  • 給屬性重新賦值成對象,是否是響應(yīng)式的?
  • 給 Vue 實(shí)例新增一個(gè)成員是否是響應(yīng)式的?
    檢測變化的注意事項(xiàng)

Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容