Vue源码实现--VNode与挂载

2018-05-27  本文已影响0人  勤奋的大鱼

 上一篇讲到当与页面渲染相关的依赖发生变化时,就会触发render watcher的run方法执行,重新收集依赖,而render watcher是把updateComponent方法作为watcher的getter,因此每次页面渲染所需的数据发生改变时,都会执行updateComponent方法。
updateComponent方法很简单

updateComponent = function () {
   vm._update(vm._render())
}

vm._render方法上一篇已经说过了,返回的是一个根据render函数生成的vnode对象,这里主要看一下vm._update以及相关的方法。

Vue.prototype._update = function (vnode) {
  var vm = this
  var prevVnode = vm._vnode
  vm._vnode = vnode
  if (!prevVnode) { // 首次渲染
    vm.$el = this.__patch__(vm.$el, vnode)
  } else {
    vm.$el = this.__patch__(prevVnode, vnode)
  }
}
// 挂载、更新vnode到页面上
Vue.prototype.__patch__ = function (oldVnode, vnode, parentElm, refElm) {
  var isRealElement = oldVnode.nodeType
  if (!isRealElement && sameVnode(oldVnode, vnode)) {
    patchVnode(oldVnode, vnode)
  } else {
    // 若isRealElement为true,说明传入的oldVnode为真实的dom节点,则生成一个空vnode实例,其中vnode.elm为传入的dom
    if (isRealElement) {
      oldVnode = emptyNodeAt(oldVnode)
    }
    var oldElm = oldVnode.elm
    var parentElm = nodeOps.parentNode(oldElm)
    createElm(vnode, parentElm, nodeOps.nextSibling(oldElm))
    // 移除老的dom元素
    removeVnodes(parentElm, [oldVnode], 0, 0)
  }
}
// 创建真实dom,refElm为插入在哪个元素之前
function createElm (vnode, parentElm, refElm) {
  var data = vnode.data
  // 创建节点
  if (vnode.tag) {
    vnode.elm = nodeOps.createElement(vnode.tag)
    // 创建子节点
    createChildren(vnode, vnode.children)
    // 添加attr
    if (data) {
      updateAttrs(emptyNode, vnode)
    }
    // 插入到文档中
    insert(parentElm, vnode.elm, refElm)
  } else {
    vnode.elm = nodeOps.createTextNode(vnode.text)
    insert(parentElm, vnode.elm, refElm)
  }
}

大概逻辑是这样的:
1.根据vm实例上是否有_vnode属性判断是否是首次渲染,若是首次渲染,则传给patch方法的是要被挂载到的dom元素,以及当前的vnode。
2.在patch方法中,若传入的oldVnode是一个真实的dom,则为这个dom元素生成一个vnode对象(vnode.elm传入的dom节点),接下来的处理和新旧两个vnode不是同一个vnode的情况相同,插入新的vnode到文档中,并且删除旧的vnode。
附上本文详细的代码注释与demo(相关代码

上一篇 下一篇

猜你喜欢

热点阅读