vue源码分析(十八)核心函数之patch

我们先打开文件src/core/vdom/patch.js。代码如下:

function patch (oldVnode, vnode, hydrating, removeOnly) {
   // vnode不存在那就销毁旧的vnode
    if (isUndef(vnode)) {
      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
      return
    }

    let isInitialPatch = false
    const insertedVnodeQueue = []
    // 如果旧的vnode不存在,那直接创建新的DOM
    if (isUndef(oldVnode)) {
      // empty mount (likely as component), create new root element
      isInitialPatch = true
      createElm(vnode, insertedVnodeQueue)
    } else {
      const isRealElement = isDef(oldVnode.nodeType)
      if (!isRealElement && sameVnode(oldVnode, vnode)) {
        // patch existing root node
        patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
      } else {
        if (isRealElement) {
          // mounting to a real element
          // check if this is server-rendered content and if we can perform
          // a successful hydration.
          if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
            oldVnode.removeAttribute(SSR_ATTR)
            hydrating = true
          }
          if (isTrue(hydrating)) {
            if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
              invokeInsertHook(vnode, insertedVnodeQueue, true)
              return oldVnode
            } else if (process.env.NODE_ENV !== 'production') {
              warn(
                'The client-side rendered virtual DOM tree is not matching ' +
                'server-rendered content. This is likely caused by incorrect ' +
                'HTML markup, for example nesting block-level elements inside ' +
                '

, or missing . Bailing hydration and performing ' + 'full client-side render.' ) } } // 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) // 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) ) // 递归更新父占位符的节点元素 if (isDef(vnode.parent)) { let ancestor = vnode.parent const patchable = isPatchable(vnode) while (ancestor) { for (let i = 0; i < cbs.destroy.length; ++i) { cbs.destroy[i](ancestor) } ancestor.elm = vnode.elm if (patchable) { for (let i = 0; i < cbs.create.length; ++i) { cbs.create[i](emptyNode, ancestor) } // #6513 // invoke insert hooks that may have been merged by create hooks. // e.g. for directives that uses the "inserted" hook. const insert = ancestor.data.hook.insert if (insert.merged) { // start at index 1 to avoid re-invoking component mounted hook for (let i = 1; i < insert.fns.length; i++) { insert.fns[i]() } } } else { registerRef(ancestor) } ancestor = ancestor.parent } } // 销毁旧的虚拟node节点 if (isDef(parentElm)) { removeVnodes([oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) } } } // 调用‘insert’钩子 invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch) return vnode.elm }

接下来我们就来拆分一下这个函数。

 if (isUndef(vnode)) {
   if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
    return
 }

如果新的vnode不存在的话,那就直接销毁之前的oldVnode,然后return掉了。

if (isUndef(oldVnode)) {
  // empty mount (likely as component), create new root element
  isInitialPatch = true
  createElm(vnode, insertedVnodeQueue)
}

如果没有存在oldVnode,那就直接根据vnode创建真实的DOM。

 const isRealElement = isDef(oldVnode.nodeType)
 if (!isRealElement && sameVnode(oldVnode, vnode)) {
   // patch existing root node
   patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
 }

我们看到这里有一个判断是否为真实DOM节点,大家可去了解DOM的nodeType属性。接下来判断如果是虚拟节点并且是同一个虚拟节点,那就调用patchVnode处理,关于patchVnode的具体代码,请点击查看。

if (isRealElement) {
  if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
    oldVnode.removeAttribute(SSR_ATTR)
    hydrating = true
  }
  if (isTrue(hydrating)) {
    if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
      invokeInsertHook(vnode, insertedVnodeQueue, true)
      return oldVnode
    } else if (process.env.NODE_ENV !== 'production') {
      warn(
       'The client-side rendered virtual DOM tree is not matching ' +
        'server-rendered content. This is likely caused by incorrect ' +
        'HTML markup, for example nesting block-level elements inside ' +
        '

, or missing . Bailing hydration and performing ' + 'full client-side render.' ) } } oldVnode = emptyNodeAt(oldVnode) }

可以看到如果是真实DOM才会进来。
oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR) 如果是‘div’并且存在‘ssr’属性。

oldVnode = emptyNodeAt(oldVnode)

新建一个空的虚拟节点,执行这行代码的前提是:
1、不存在服务器渲染
2、hydrating 的值为false

// 销毁旧的虚拟node节点
        if (isDef(parentElm)) {
          removeVnodes([oldVnode], 0, 0)
        } else if (isDef(oldVnode.tag)) {
          invokeDestroyHook(oldVnode)
        }

// 调用‘insert’钩子 
    invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)

最后还会进行 ‘销毁旧的虚拟node节点’、‘调用‘insert’钩子 ’。

你可能感兴趣的:(vue源码分析(十八)核心函数之patch)