美文网首页
Vue源码实现--依赖收集与页面渲染

Vue源码实现--依赖收集与页面渲染

作者: 勤奋的大鱼 | 来源:发表于2018-05-27 16:31 被阅读0次

     首先,给Watcher对象做点改动:

    Watcher.prototype.update = function () {
      // 若是lazy Watcher, 只需把dirty设置为true,否则若sync为true,直接执行run,否则就在nextTick中执行run方法,在这里暂时不考虑queueWatcher情况
      if (this.lazy) {
        this.dirty = true
      } else {
        this.run()
      }
    }
    Watcher.prototype.run = function () {
      const oldValue = this.value
      // 每次执行run方法时都会调用get方法重新收集依赖
      const value = this.get()
      this.value = value
      // 执行watcher的回调
      if (this.cb) {
        this.cb.call(this.vm, value, oldValue)
      }
    }
    // 在dep.depend中调用
    Watcher.prototype.addDep = function (dep) {
      if (!this.depIds.has(dep.id)) {
        this.deps.push(dep)
        this.depIds.add(dep.id)
        dep.addSub(this)
      }
    }
    

     可以看到,在每次Watcher.run执行的时候,都会执行this.get方法获取最新的值,而执行get方法就会重新收集该watcher的相关依赖,那么问题就来了,每次依赖的数据发生改变,都会重新触发依赖收集,那么该数据的dep中就会存在重复的watcher。所以我们为Dep加上uid的静态属性,每次实例化时候+1,在Watcher依赖收集的时候判断是否已经存在该dep,避免重复的依赖收集。
     另外,在vue的源码中,Watcher中还有newDepIds,newDeps这两个属性,据我理解,是为了重新依赖收集时去除没必要的依赖,这里暂不实现。

    重点来了

     要怎样收集页面渲染所需的依赖,使页面渲染相关的数据发生改变就触发页面的重新渲染呢。其实关键的代码就这么一句

    vm._watcher = new Watcher(vm, updateComponent)
    

    这样,updateComponent需要用到的相关数据就会被收集到render Watcher(vm._watcher)中,每次数据发生改变就会重新调用updateComponent方法。

    VNode

    关于VNode部分,暂时只实现一些目前需要用到的东西

      var VNode = function (tag, data, children, text, elm, context) {
        this.tag = tag
        this.data = data
        this.children = children
        this.text = text
        this.elm = elm
        this.context = context
      }
      function createTextVNode (val) {
        return new VNode(undefined, undefined, undefined, val)
      }
      Vue.prototype.$createElement = function (tag, data, children) {
        var text, vnode, _children = children || []
        // 处理render函数中的文字
        _children.forEach(function (item, n) {
          if (typeof item === 'string' || typeof item === 'number') {
            _children.splice(n, 1, createTextVNode(item))
          }
        })
        if (typeof tag === 'string') {
          vnode = new VNode(tag, data, _children)
        }
        return vnode
      }
    

     都知道,vue包括完整版和运行时两个版本,区别在于完整版包括运行时+编译器,就是把模板编译成render函数,因此编译器对于vue来说并不是绝对必要的,这里暂不去实现。
     如果是运行时版本,那么就需要自己直接写render函数了,关于render函数可参考官方文档。以上代码就可以把render函数处理成VNode对象。
     附上本文详细的代码与注释(相关代码

    相关文章

      网友评论

          本文标题:Vue源码实现--依赖收集与页面渲染

          本文链接:https://www.haomeiwen.com/subject/ewifqftx.html