vue源码分析-11-渲染之Vue.prototype._update

1,605 阅读2分钟

概述

render函数执行以后会生成虚拟节点Vnode,Vnode以参数传入_update方法,此方法的作用就是更新或渲染真实的dom节点

vm._update(vm._render(), hydrating)

我们看一下_update方法的实现,主要是调用了__patch__方法,__patch__方法就负责生成/更新真实的dom,然后将dom对象赋值给vm.$el

    // 如果是首次渲染
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // 不是首次渲染
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode)
    }

__patch__方法定义在runtime/index.js中

// 挂在patch方法
Vue.prototype.__patch__ = inBrowser ? patch : noop

patch

__patch__方法是由createPatchFunction方法返回的一个函数

export const patch: Function = createPatchFunction({ nodeOps, modules })

我们可以看到createPatchFunction方法很长,定义了许多方法,我们直接看最终返回的函数

此函数的主要逻辑就是区分首次渲染和更新渲染,首次渲染的话,就会调用createElm创建一个新的dom节点,如果是更新渲染的话,就会调用patchVnode对比新旧节点,然后更新dom,这就是所说的dom diff算法

// 返回patch函数,执行这个函数可以实际更新dom
  return function patch (oldVnode, vnode, hydrating, removeOnly) {
    // 如果新的vnode不存在,老的vnode存在,那么销毁老的vnode,然后返回undefined
    // 之后的vm.$el = undefined那么界面这个元素就会被销毁
    if (isUndef(vnode)) {
      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
      return
    }

    let isInitialPatch = false
    const insertedVnodeQueue = []

    // 如果oldVnode未定义说明是创建
    if (isUndef(oldVnode)) {
      // 如果是新创建的组件 的话
      // empty mount (likely as component), create new root element
      isInitialPatch = true
      // 创建元素
      createElm(vnode, insertedVnodeQueue)
    }
    // 根实例会走else,因为vm.$el不为空,也就是第一次渲染会进else
    else
      {
        // 是否是真实的dom
      const isRealElement = isDef(oldVnode.nodeType)
      if (!isRealElement && sameVnode(oldVnode, vnode)) {
        // patch existing root node
        // diff算法,新旧节点比对
        patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
      } else 
      {
        // 创建根组件会走这个逻辑,因为首次渲染调用vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
        // 传入的是一个真实根节点
        // 是真实的dom
        if (isRealElement) {
          /* 服务端渲染处理代码省略 */
         
          // either not server-rendered, or hydration failed.
          // create an empty node and replace it
          // 创建一个空的节点
          oldVnode = emptyNodeAt(oldVnode)
        }

        // replacing existing element
        const oldElm = oldVnode.elm
        const parentElm = nodeOps.parentNode(oldElm)

        // 创建一个新的dom节点
        // create new node
        createElm(
          vnode,
          insertedVnodeQueue,
          // extremely rare edge case: do not insert if old element is in a
          // leaving transition. Only happens when combining transition +
          // keep-alive + HOCs. (#4590)
          oldElm._leaveCb ? null : parentElm,
          nodeOps.nextSibling(oldElm)
        )
        
        /* 非重要代码省略 */
      }
       
    }

    invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
    return vnode.elm
  }