vue源码分析(二十)核心函数之__patch__(更新视图)

我们打开文件 src/core/vdom/patch.js,找到定义Observer函数的代码:

 return function patch (oldVnode, vnode, hydrating, removeOnly) {
    if (isUndef(vnode)) {
      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
      return
    }

    let isInitialPatch = false
    const insertedVnodeQueue = []

    if (isUndef(oldVnode)) {
      // empty mount (likely as component), create new root element
     // 如果没有老的node节点,就用新的vnode节点去创建。
      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) ) // update parent placeholder node element, recursively 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 } } // destroy old node if (isDef(parentElm)) { removeVnodes([oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) } } } invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch) return vnode.elm } }

首先如果最新值是 undefined或者 null并且旧值不是undefined或者 null, 那就调用 invokeDestroyHook函数(解绑它的指令及事件监听器)。
接下来我们详细分析

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

如果没有老的node节点,就用新的vnode节点去创建。createElm 是创建节点的函数。
接下来就是存在 oldVnode的情况,

const isRealElement = isDef(oldVnode.nodeType)

首先判断是否是真实节点 ,因为每个几都有 nodeType属性具体的节点类型请点击查看。

 if (isRealElement) {
  // 省略部分代码
  oldVnode = emptyNodeAt(oldVnode)
}

省略的都是关于 ssr判断是代码,浏览器端的话就不用管了。
最后看到,就新建一个空的新节点赋值给了 oldVnode

// replacing existing element
const oldElm = oldVnode.elm
const parentElm = nodeOps.parentNode(oldElm)
// create new node
createElm(vnode,oldElm._leaveCb ? null : parentElm,nodeOps.nextSibling(oldElm))

定义了 oldElmparentElm 分别保存旧的真实DOM节点和旧的真实DOM节点的父节点。
然后就是调用 createElm 函数创建节点了。可以看到真实的DOM节点被插入到页面了。

// destroy old node
 if (isDef(parentElm)) {
    removeVnodes([oldVnode], 0, 0)
 } else if (isDef(oldVnode.tag)) {
    invokeDestroyHook(oldVnode)
 }

移除旧的DOM节点,解绑它的指令及事件监听器。
最后返回真实的DOM节点给 $el 属性。

你可能感兴趣的:(vue源码分析(二十)核心函数之__patch__(更新视图))