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等出现频率较高的函数。写这样一个最简实现主要是为了梳理一下主干,下降阅读源码的难度。

相关文章
相关标签/搜索