vue源码细读 -- 虚拟dom分解 之 patch.js 分解二

createElm方法分解

function createElm (
  vnode,
  insertedVnodeQueue,
  parentElm,
  refElm,
  nested,
  ownerArray,
  index
) {
  if (isDef(vnode.elm) && isDef(ownerArray)) {
    // This vnode was used in a previous render!
    // now it's used as a new node, overwriting its elm would cause
    // potential patch errors down the road when it's used as an insertion
    // reference node. Instead, we clone the node on-demand before creating
    // associated DOM element for it.
    vnode = ownerArray[index] = cloneVNode(vnode)
  }

  vnode.isRootInsert = !nested // for transition enter check
  if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
    return
  }

  const data = vnode.data
  const children = vnode.children
  const tag = vnode.tag
  if (isDef(tag)) {
    if (process.env.NODE_ENV !== 'production') {
      if (data && data.pre) {
        creatingElmInVPre++
      }
      if (isUnknownElement(vnode, creatingElmInVPre)) {
        warn(
          'Unknown custom element: <' + tag + '> - did you ' +
          'register the component correctly? For recursive components, ' +
          'make sure to provide the "name" option.',
          vnode.context
        )
      }
    }

    vnode.elm = vnode.ns
      ? nodeOps.createElementNS(vnode.ns, tag)
      : nodeOps.createElement(tag, vnode)
    setScope(vnode)

    /* istanbul ignore if */
    if (__WEEX__) {
      // in Weex, the default insertion order is parent-first.
      // List items can be optimized to use children-first insertion
      // with append="tree".
      const appendAsTree = isDef(data) && isTrue(data.appendAsTree)
      if (!appendAsTree) {
        if (isDef(data)) {
          invokeCreateHooks(vnode, insertedVnodeQueue)
        }
        insert(parentElm, vnode.elm, refElm)
      }
      createChildren(vnode, children, insertedVnodeQueue)
      if (appendAsTree) {
        if (isDef(data)) {
          invokeCreateHooks(vnode, insertedVnodeQueue)
        }
        insert(parentElm, vnode.elm, refElm)
      }
    } else {
      createChildren(vnode, children, insertedVnodeQueue)
      if (isDef(data)) {
        invokeCreateHooks(vnode, insertedVnodeQueue)
      }
      insert(parentElm, vnode.elm, refElm)
    }

    if (process.env.NODE_ENV !== 'production' && data && data.pre) {
      creatingElmInVPre--
    }
  } else if (isTrue(vnode.isComment)) {
    vnode.elm = nodeOps.createComment(vnode.text)
    insert(parentElm, vnode.elm, refElm)
  } else {
    vnode.elm = nodeOps.createTextNode(vnode.text)
    insert(parentElm, vnode.elm, refElm)
  }
}

第一个模块

if (isDef(vnode.elm) && isDef(ownerArray)) {
  // This vnode was used in a previous render!
  // now it's used as a new node, overwriting its elm would cause
  // potential patch errors down the road when it's used as an insertion
  // reference node. Instead, we clone the node on-demand before creating
  // associated DOM element for it.
  vnode = ownerArray[index] = cloneVNode(vnode)
}

注释一下

// 如果vnode.elm存在并且 ownerArray存在
if (isDef(vnode.elm) && isDef(ownerArray)) {
  // vnode和owernerArray[index]同时指向clone的新的vnode
  vnode = ownerArray[index] = cloneVNode(vnode)
}

第二个模块

vnode.isRootInsert = !nested // for transition enter check
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
  return
}

注释一下

vnode.isRootInsert = !nested
  // 如果创建模块返回true,则直接return
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
  return
}

第三个模块

const data = vnode.data
const children = vnode.children
const tag = vnode.tag
if (isDef(tag)) {
  if (process.env.NODE_ENV !== 'production') {
    if (data && data.pre) {
      creatingElmInVPre++
    }
    if (isUnknownElement(vnode, creatingElmInVPre)) {
      warn(
        'Unknown custom element: <' + tag + '> - did you ' +
        'register the component correctly? For recursive components, ' +
        'make sure to provide the "name" option.',
        vnode.context
      )
    }
  }

  vnode.elm = vnode.ns
    ? nodeOps.createElementNS(vnode.ns, tag)
    : nodeOps.createElement(tag, vnode)
  setScope(vnode)

  /* istanbul ignore if */
  if (__WEEX__) {
    // in Weex, the default insertion order is parent-first.
    // List items can be optimized to use children-first insertion
    // with append="tree".
    const appendAsTree = isDef(data) && isTrue(data.appendAsTree)
    if (!appendAsTree) {
      if (isDef(data)) {
        invokeCreateHooks(vnode, insertedVnodeQueue)
      }
      insert(parentElm, vnode.elm, refElm)
    }
    createChildren(vnode, children, insertedVnodeQueue)
    if (appendAsTree) {
      if (isDef(data)) {
        invokeCreateHooks(vnode, insertedVnodeQueue)
      }
      insert(parentElm, vnode.elm, refElm)
    }
  } else {
    createChildren(vnode, children, insertedVnodeQueue)
    if (isDef(data)) {
      invokeCreateHooks(vnode, insertedVnodeQueue)
    }
    insert(parentElm, vnode.elm, refElm)
  }

  if (process.env.NODE_ENV !== 'production' && data && data.pre) {
    creatingElmInVPre--
  }
}

注释一下

const data = vnode.data
const children = vnode.children
const tag = vnode.tag
// 如果vnode.tag存在
if (isDef(tag)) {
  // 为vnode.elm创建节点
  vnode.elm = vnode.ns
    ? nodeOps.createElementNS(vnode.ns, tag)
    : nodeOps.createElement(tag, vnode)
  setScope(vnode)
  // 创建子节点
  createChildren(vnode, children, insertedVnodeQueue)
  // 如果vnode.data存在,创建钩子方法
  if (isDef(data)) {
    invokeCreateHooks(vnode, insertedVnodeQueue)
  }
  // 向父节点插入该节点
  insert(parentElm, vnode.elm, refElm)
}

第四个模块

  // 如果vnode.isComment
else if (isTrue(vnode.isComment)) {
  // 为vnode.elm 创建模块
  vnode.elm = nodeOps.createComment(vnode.text)
  // 向父节点插入该节点
  insert(parentElm, vnode.elm, refElm)
}

第六个模块

else {
  // 创建文本节点
  vnode.elm = nodeOps.createTextNode(vnode.text)
  // 向父节点插入该节点
  insert(parentElm, vnode.elm, refElm)
}

整理

接收参数说明

  • vnode:虚拟节点
  • insertedVnodeQueue:插入虚拟节点的队列
  • parentElm:父节点
  • ownerArray:如果vnode.elm存在就要被更新vnode的对象
  • index:vnode在ownerArray中的位置

方法作用整理

  • 一:如果vnode.elm存在并且 ownerArray存在,vnode和owernerArray[index]同时指向clone的新的vnode
  • 二:如果创建模块createComponent返回true,则直接return
  • 三:如果vnode.tag存在,说明是个标签,创建dom。如果vnode.data中有值,加入钩子方法。然后向父节点插入当前dom
  • 四:如果vnode.isComment,按照createComment 创建,然后插入父节点
  • 五:否则按照createTextNode创建,然后插入父节点

总结

这个方法是用来修改vnode,并且向父节点插入当前vnode的dom节点的。

随机浏览