[基礎向]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部分的兩個連接ui

  1. 建立vue實例,執行new Vue()
  2. 對data進行初始化,對data中屬性進行屬性劫持
    • 劫持過程當中,在閉包內建立對當前屬性的依賴隊列(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,則觸發msgset
    1. set先修改msg的值
    2. 再執行msg依賴隊列中的全部watcher的函數,即vText,頁面的innerText被同步更新

總結

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

而vue的真正的執行過程毫不是上面寫的這麼簡單,好比watcher的執行就毫不是簡單的遍歷執行,並且還對observer進行了很大程度的簡化。省略了諸如_proxydefineReactive等函數。spa

寫這樣一個最簡實現主要是爲了梳理一下主幹,下降閱讀源碼的難度。.net

相關文章
相關標籤/搜索