簡單理解Vue響應式原理

該文章簡單模擬Vue響應式原理(MVVM),筆者還只是個大三的學生,文章有不少不到之處歡迎各位大佬指出錯誤,多多交流。html

這是Vue官方文檔的響應式原理圖



官方文檔的東西對於初學者來講都有點晦澀難懂,本文就將以一種簡單的方式來聊聊Vue的響應式原理。前端

問題提出

Vuejs是數據驅動型,數據發生改變界面也會刷新改變。但這並非理所固然,在其內部作了不少複雜的操做。vue

  • 思路react

    1. 首先要搞懂Vue內部是如何監聽數據的改變?bash

      經過Object.definePropety這個方法來監聽數據的改變。app

      這個方法的第三個參數的 Setters 和 Getters是關鍵。詳情ide

    2. 已經監聽了數據的改變,Vue是如何知道要通知哪些元素界面發生刷新呢?函數

      經過發佈訂閱者模式。ui


    Observer會監聽全部的data屬性而且給他們建立一一對應的Dep對象.this

    Compile 會解析模板中指令(同時也會將界面初始化)。 一個指令就建立一個Watcher對象, 而後添加到相對應的Dep對象的訂閱中綁定更新函數。若是一個屬性的value發生改變Observer就會通知所對應的Dep對象調用notify()通知全部的訂閱者 更新界面。

    詳情看下圖

    PS:畫圖技術渣渣,見諒。

具體實現

筆者只是簡單模擬,尚未達到能夠剖析源碼的程度。

  • 先用正則作下Mustache語法(雙大括號)轉化。
//render.js
var render = function(template, data) {
  const reg = /\{\{(\w+)\}\}/; //不作貪婪匹配 
  if (reg.test(template)) {  //退出條件 false
     //是否須要編譯
    // vue源碼模板編譯用的正則方法
    const key = reg.exec(template)[1]
    // console.log(key)
    template = template.replace(reg, data[key]);
    return  render(template, data)  //遞歸渲染
  }

  // template.replace(/{{(.)+/)
  return template
}
複製代碼
  • 上代碼(代碼有挺詳細的註釋)。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>

  <div id="app">
   
  </div>

  <script src="./render.js"></script>
  <script>
    const obj = {  //模擬數據
      name: '張三',
      age: '18',
      sex: '男'
    }

   
    // 發佈訂閱者模式
    class Dep {
      constructor() {
        this.subs = []
      }
        
      addSub(watch) { //添加訂閱方法
        this.subs.push(watch)
      }

      notify() { //通知訂閱者更新
        this.subs.forEach(item => {
          item.update();
        })
      }
    }
    class Watch { 
      constructor(name) {
        this.name = name;
      }
      update() {
        console.log(this.name + 'update')
        //更新視圖
        document.getElementById('app').innerHTML = render(template,obj)  
      }
    }
    
    Object.keys(obj).forEach(key => {  //遍歷obj對象
      let dep = new Dep; //建立發佈者對象
      let value = obj[key];
      Object.defineProperty(obj, key, {//監聽數據
        set: function(newValue) {  //數據改變
          console.log( '數據' +  key + '改變了')
          // 更新數據
          value = newValue
          dep.notify() //通知訂閱者更新
          
        },
        get: function() { //獲取數據
          console.log('數據' + key + '加入了響應式系統')
          let w = new Watch(value)
          dep.addSub(w); 
          return value
        }
      })
    })

    obj.message = "沒用的"
    //待編譯的模板
    var template = '我是{{name}}, {{name}} 年齡 {{age}},性別 {{sex}} 。 {{message}} '
    document.getElementById('app').innerHTML = render(template,obj);

  </script>

</body>
</html>
複製代碼
  • 效果圖以下

模板編譯將obj初始化的數據都加入了響應式系統。

obj.message = "沒用的"這個數據沒在初始化中,全部沒有打印。即不是響應式屬性。

Vue 不容許動態添加根級響應式屬性,因此你必須在初始化實例前聲明全部根級響應式屬性。詳情看文檔聲明響應式屬性


頁面成功的發生了刷新。看到這差很少就能初步瞭解了響應式原理吧!


再次證實下message不是響應式屬性。咱們在Vue開發中要時刻注意這點,能夠避免不少坑。


最後

筆者是一個入門前端不久的小白,這篇文章挺淺顯的,可能也有一些錯誤,但願大佬們多多給點建議。若是這篇文章對你有那麼一點點小幫助的話,不妨點個贊吧。

相關文章
相關標籤/搜索