Vue主要原理最簡實現與邏輯梳理

Vue的主要原理中主要用到了定義的這麼幾個函數Dep,Watcher,observer。
咱們來使用這幾個函數簡單的實現一下vue構造函數數據綁定和相互依賴部分,梳理一下它們之間的關係。
省略了編譯部分和proxy代理與其餘的一些複雜邏輯。html

Dep

Dep是依賴類,簡要實現爲vue

class Dep {
  constructor () {
    // 放當時屬性的觀察者
    this.subs = []
  }
}
// target 用來掛載當時的watcher觀察者
Dep.target = null

observer

作屬性劫持,並作點其餘事情git

function observer (vm, key, val) {
  let dep = new Dep()
  Object.defineProperty(vm, key, {
    /**
     * get主要作兩個事情
     * 1. 收集觀察當前key的wathcer(即依賴當前key的操做)
     * 2. 獲取值
     */
    get () {
      // 這是做用1
      if (Dep.target) {
        dep.subs.push(Dep.target)
      }
      // 這是做用2
      return val
    },
    /**
     * set也是兩個事情
     * 1. 修改目標值
     * 2. 執行依賴當前key的watcher
     */
    set (newVal) {
      // 這是做用1
      val = newVal
      // 這是做用2
      for(cb of dep.subs) {
        cb.call(vm)
      }
    }
  })
}

Watcher

Watcher是觀察者類,用來建立依賴某屬性的操做(如指令,渲染,計算屬性等)github

class Watcher {
  /**
   * vm: 實例
   * cb: 依賴某屬性的操做函數
   */
  constructor (vm, cb) {
    // 把當前的操做掛載到Dep上
    Dep.target = cb
    /**
     * 執行操做,兩個做用
     * 1. 進行操做的初始化
     * 2. 觸發屬性的get方法,使當前cb被收集
     */
    cb.call(vm)
    Dep.target = null
  }
}

demo

那麼咱們就使用上面定義好的函數寫個例子閉包

<div>
  <p class="text"></p>
<div>
let vm = new Vue({
  // 假設有data
  data: {msg: 1},
  // 有某個v-text操做,咱們抽象爲vText函數,依賴屬性msg(表明全部依賴其餘屬性的操做)
  renderFun: {
    vText () {
      document.querySelector('.text').innerText = this.msg
    }
  }
})
// 修改vue實例的值,觀察變化
vm.msg = 333

那麼咱們也寫一個vue的簡易構造函數函數

class Vue {
  constructor (options) {
    let data = options.data
    let renderFun = options.renderFun
    // initData
    Object.keys(data).forEach(key => {
      observer(this, key, data[key])
    })
    // 模擬計算屬性,watcher,指令等依賴屬性的操做
    Object.keys(renderFun).forEach(key => {
      new Watcher(this, renderFun[key])
    })
  }
}

執行過程

完整的代碼能夠看demo部分的兩個連接this

  1. 建立vue實例,執行new Vue()
  2. 對data進行初始化,對data中屬性進行屬性劫持.net

    • 劫持過程當中,在閉包內建立對當前屬性的依賴隊列(dep.subs)和值(val)。get進行觀察者watcher的收集和值得獲取;set進行值的更新和依賴隊列中watcher的執行
  3. 對編譯過程當中如computed\watcher模板編譯過程當中的指令函數進行初始化,咱們以renderFun代替
  4. 針對renderFun中的每一個功能函數進行new Watcher()工做
  5. vText爲例子,在new Wathcer()過程當中代理

    1. vText掛載到全局通用的Dep.target
    2. 執行vText,其中有讀vm.msg的操做,則觸發msg屬性的get,進入Dep.target判斷,將Dep.targetvText收集進msgsubs依賴隊列中,此時vText執行完畢,頁面innetText被修改
    3. Dep.target置空
  6. 執行vm.msg = 333,則觸發msgsetcode

    1. set先修改msg的值
    2. 再執行msg依賴隊列中的全部watcher的函數,即vText,頁面的innerText被同步更新

總結

總之幾者的關係就是在observerget中將對當前屬性的watcher收集進dep,在observerset中執行收集到的watcher

而vue的真正的執行過程毫不是上面寫的這麼簡單,好比watcher的執行就毫不是簡單的遍歷執行,並且還對observer進行了很大程度的簡化。咱們還省略了諸如_proxydefineReactive等出現頻率較高的函數。寫這樣一個最簡實現主要是爲了梳理一下主幹,下降閱讀源碼的難度。

相關文章
相關標籤/搜索