src/core/vdom/patch.ts TYPESCRIPT 908 lines View on github.com → Search inside
1/**2 * Virtual DOM patching algorithm based on Snabbdom by3 * Simon Friis Vindum (@paldepind)4 * Licensed under the MIT License5 * https://github.com/paldepind/snabbdom/blob/master/LICENSE6 *7 * modified by Evan You (@yyx990803)8 *9 * Not type-checking this because this file is perf-critical and the cost10 * of making flow understand it is not worth it.11 */1213import VNode, { cloneVNode } from './vnode'14import config from '../config'15import { SSR_ATTR } from 'shared/constants'16import { registerRef } from './modules/template-ref'17import { traverse } from '../observer/traverse'18import { activeInstance } from '../instance/lifecycle'19import { isTextInputType } from 'web/util/element'2021import {22  warn,23  isDef,24  isUndef,25  isTrue,26  isArray,27  makeMap,28  isRegExp,29  isPrimitive30} from '../util/index'3132export const emptyNode = new VNode('', {}, [])3334const hooks = ['create', 'activate', 'update', 'remove', 'destroy']3536function sameVnode(a, b) {37  return (38    a.key === b.key &&39    a.asyncFactory === b.asyncFactory &&40    ((a.tag === b.tag &&41      a.isComment === b.isComment &&42      isDef(a.data) === isDef(b.data) &&43      sameInputType(a, b)) ||44      (isTrue(a.isAsyncPlaceholder) && isUndef(b.asyncFactory.error)))45  )46}4748function sameInputType(a, b) {49  if (a.tag !== 'input') return true50  let i51  const typeA = isDef((i = a.data)) && isDef((i = i.attrs)) && i.type52  const typeB = isDef((i = b.data)) && isDef((i = i.attrs)) && i.type53  return typeA === typeB || (isTextInputType(typeA) && isTextInputType(typeB))54}5556function createKeyToOldIdx(children, beginIdx, endIdx) {57  let i, key58  const map = {}59  for (i = beginIdx; i <= endIdx; ++i) {60    key = children[i].key61    if (isDef(key)) map[key] = i62  }63  return map64}6566export function createPatchFunction(backend) {67  let i, j68  const cbs: any = {}6970  const { modules, nodeOps } = backend7172  for (i = 0; i < hooks.length; ++i) {73    cbs[hooks[i]] = []74    for (j = 0; j < modules.length; ++j) {75      if (isDef(modules[j][hooks[i]])) {76        cbs[hooks[i]].push(modules[j][hooks[i]])77      }78    }79  }8081  function emptyNodeAt(elm) {82    return new VNode(nodeOps.tagName(elm).toLowerCase(), {}, [], undefined, elm)83  }8485  function createRmCb(childElm, listeners) {86    function remove() {87      if (--remove.listeners === 0) {88        removeNode(childElm)89      }90    }91    remove.listeners = listeners92    return remove93  }9495  function removeNode(el) {96    const parent = nodeOps.parentNode(el)97    // element may have already been removed due to v-html / v-text98    if (isDef(parent)) {99      nodeOps.removeChild(parent, el)100    }101  }102103  function isUnknownElement(vnode, inVPre) {104    return (105      !inVPre &&106      !vnode.ns &&107      !(108        config.ignoredElements.length &&109        config.ignoredElements.some(ignore => {110          return isRegExp(ignore)111            ? ignore.test(vnode.tag)112            : ignore === vnode.tag113        })114      ) &&115      config.isUnknownElement(vnode.tag)116    )117  }118119  let creatingElmInVPre = 0120121  function createElm(122    vnode,123    insertedVnodeQueue,124    parentElm?: any,125    refElm?: any,126    nested?: any,127    ownerArray?: any,128    index?: any129  ) {130    if (isDef(vnode.elm) && isDef(ownerArray)) {131      // This vnode was used in a previous render!132      // now it's used as a new node, overwriting its elm would cause133      // potential patch errors down the road when it's used as an insertion134      // reference node. Instead, we clone the node on-demand before creating135      // associated DOM element for it.136      vnode = ownerArray[index] = cloneVNode(vnode)137    }138139    vnode.isRootInsert = !nested // for transition enter check140    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {141      return142    }143144    const data = vnode.data145    const children = vnode.children146    const tag = vnode.tag147    if (isDef(tag)) {148      if (__DEV__) {149        if (data && data.pre) {150          creatingElmInVPre++151        }152        if (isUnknownElement(vnode, creatingElmInVPre)) {153          warn(154            'Unknown custom element: <' +155              tag +156              '> - did you ' +157              'register the component correctly? For recursive components, ' +158              'make sure to provide the "name" option.',159            vnode.context160          )161        }162      }163164      vnode.elm = vnode.ns165        ? nodeOps.createElementNS(vnode.ns, tag)166        : nodeOps.createElement(tag, vnode)167      setScope(vnode)168169      createChildren(vnode, children, insertedVnodeQueue)170      if (isDef(data)) {171        invokeCreateHooks(vnode, insertedVnodeQueue)172      }173      insert(parentElm, vnode.elm, refElm)174175      if (__DEV__ && data && data.pre) {176        creatingElmInVPre--177      }178    } else if (isTrue(vnode.isComment)) {179      vnode.elm = nodeOps.createComment(vnode.text)180      insert(parentElm, vnode.elm, refElm)181    } else {182      vnode.elm = nodeOps.createTextNode(vnode.text)183      insert(parentElm, vnode.elm, refElm)184    }185  }186187  function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {188    let i = vnode.data189    if (isDef(i)) {190      const isReactivated = isDef(vnode.componentInstance) && i.keepAlive191      if (isDef((i = i.hook)) && isDef((i = i.init))) {192        i(vnode, false /* hydrating */)193      }194      // after calling the init hook, if the vnode is a child component195      // it should've created a child instance and mounted it. the child196      // component also has set the placeholder vnode's elm.197      // in that case we can just return the element and be done.198      if (isDef(vnode.componentInstance)) {199        initComponent(vnode, insertedVnodeQueue)200        insert(parentElm, vnode.elm, refElm)201        if (isTrue(isReactivated)) {202          reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)203        }204        return true205      }206    }207  }208209  function initComponent(vnode, insertedVnodeQueue) {210    if (isDef(vnode.data.pendingInsert)) {211      insertedVnodeQueue.push.apply(212        insertedVnodeQueue,213        vnode.data.pendingInsert214      )215      vnode.data.pendingInsert = null216    }217    vnode.elm = vnode.componentInstance.$el218    if (isPatchable(vnode)) {219      invokeCreateHooks(vnode, insertedVnodeQueue)220      setScope(vnode)221    } else {222      // empty component root.223      // skip all element-related modules except for ref (#3455)224      registerRef(vnode)225      // make sure to invoke the insert hook226      insertedVnodeQueue.push(vnode)227    }228  }229230  function reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm) {231    let i232    // hack for #4339: a reactivated component with inner transition233    // does not trigger because the inner node's created hooks are not called234    // again. It's not ideal to involve module-specific logic in here but235    // there doesn't seem to be a better way to do it.236    let innerNode = vnode237    while (innerNode.componentInstance) {238      innerNode = innerNode.componentInstance._vnode239      if (isDef((i = innerNode.data)) && isDef((i = i.transition))) {240        for (i = 0; i < cbs.activate.length; ++i) {241          cbs.activate[i](emptyNode, innerNode)242        }243        insertedVnodeQueue.push(innerNode)244        break245      }246    }247    // unlike a newly created component,248    // a reactivated keep-alive component doesn't insert itself249    insert(parentElm, vnode.elm, refElm)250  }251252  function insert(parent, elm, ref) {253    if (isDef(parent)) {254      if (isDef(ref)) {255        if (nodeOps.parentNode(ref) === parent) {256          nodeOps.insertBefore(parent, elm, ref)257        }258      } else {259        nodeOps.appendChild(parent, elm)260      }261    }262  }263264  function createChildren(vnode, children, insertedVnodeQueue) {265    if (isArray(children)) {266      if (__DEV__) {267        checkDuplicateKeys(children)268      }269      for (let i = 0; i < children.length; ++i) {270        createElm(271          children[i],272          insertedVnodeQueue,273          vnode.elm,274          null,275          true,276          children,277          i278        )279      }280    } else if (isPrimitive(vnode.text)) {281      nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)))282    }283  }284285  function isPatchable(vnode) {286    while (vnode.componentInstance) {287      vnode = vnode.componentInstance._vnode288    }289    return isDef(vnode.tag)290  }291292  function invokeCreateHooks(vnode, insertedVnodeQueue) {293    for (let i = 0; i < cbs.create.length; ++i) {294      cbs.create[i](emptyNode, vnode)295    }296    i = vnode.data.hook // Reuse variable297    if (isDef(i)) {298      if (isDef(i.create)) i.create(emptyNode, vnode)299      if (isDef(i.insert)) insertedVnodeQueue.push(vnode)300    }301  }302303  // set scope id attribute for scoped CSS.304  // this is implemented as a special case to avoid the overhead305  // of going through the normal attribute patching process.306  function setScope(vnode) {307    let i308    if (isDef((i = vnode.fnScopeId))) {309      nodeOps.setStyleScope(vnode.elm, i)310    } else {311      let ancestor = vnode312      while (ancestor) {313        if (isDef((i = ancestor.context)) && isDef((i = i.$options._scopeId))) {314          nodeOps.setStyleScope(vnode.elm, i)315        }316        ancestor = ancestor.parent317      }318    }319    // for slot content they should also get the scopeId from the host instance.320    if (321      isDef((i = activeInstance)) &&322      i !== vnode.context &&323      i !== vnode.fnContext &&324      isDef((i = i.$options._scopeId))325    ) {326      nodeOps.setStyleScope(vnode.elm, i)327    }328  }329330  function addVnodes(331    parentElm,332    refElm,333    vnodes,334    startIdx,335    endIdx,336    insertedVnodeQueue337  ) {338    for (; startIdx <= endIdx; ++startIdx) {339      createElm(340        vnodes[startIdx],341        insertedVnodeQueue,342        parentElm,343        refElm,344        false,345        vnodes,346        startIdx347      )348    }349  }350351  function invokeDestroyHook(vnode) {352    let i, j353    const data = vnode.data354    if (isDef(data)) {355      if (isDef((i = data.hook)) && isDef((i = i.destroy))) i(vnode)356      for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode)357    }358    if (isDef((i = vnode.children))) {359      for (j = 0; j < vnode.children.length; ++j) {360        invokeDestroyHook(vnode.children[j])361      }362    }363  }364365  function removeVnodes(vnodes, startIdx, endIdx) {366    for (; startIdx <= endIdx; ++startIdx) {367      const ch = vnodes[startIdx]368      if (isDef(ch)) {369        if (isDef(ch.tag)) {370          removeAndInvokeRemoveHook(ch)371          invokeDestroyHook(ch)372        } else {373          // Text node374          removeNode(ch.elm)375        }376      }377    }378  }379380  function removeAndInvokeRemoveHook(vnode, rm?: any) {381    if (isDef(rm) || isDef(vnode.data)) {382      let i383      const listeners = cbs.remove.length + 1384      if (isDef(rm)) {385        // we have a recursively passed down rm callback386        // increase the listeners count387        rm.listeners += listeners388      } else {389        // directly removing390        rm = createRmCb(vnode.elm, listeners)391      }392      // recursively invoke hooks on child component root node393      if (394        isDef((i = vnode.componentInstance)) &&395        isDef((i = i._vnode)) &&396        isDef(i.data)397      ) {398        removeAndInvokeRemoveHook(i, rm)399      }400      for (i = 0; i < cbs.remove.length; ++i) {401        cbs.remove[i](vnode, rm)402      }403      if (isDef((i = vnode.data.hook)) && isDef((i = i.remove))) {404        i(vnode, rm)405      } else {406        rm()407      }408    } else {409      removeNode(vnode.elm)410    }411  }412413  function updateChildren(414    parentElm,415    oldCh,416    newCh,417    insertedVnodeQueue,418    removeOnly419  ) {420    let oldStartIdx = 0421    let newStartIdx = 0422    let oldEndIdx = oldCh.length - 1423    let oldStartVnode = oldCh[0]424    let oldEndVnode = oldCh[oldEndIdx]425    let newEndIdx = newCh.length - 1426    let newStartVnode = newCh[0]427    let newEndVnode = newCh[newEndIdx]428    let oldKeyToIdx, idxInOld, vnodeToMove, refElm429430    // removeOnly is a special flag used only by <transition-group>431    // to ensure removed elements stay in correct relative positions432    // during leaving transitions433    const canMove = !removeOnly434435    if (__DEV__) {436      checkDuplicateKeys(newCh)437    }438439    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {440      if (isUndef(oldStartVnode)) {441        oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left442      } else if (isUndef(oldEndVnode)) {443        oldEndVnode = oldCh[--oldEndIdx]444      } else if (sameVnode(oldStartVnode, newStartVnode)) {445        patchVnode(446          oldStartVnode,447          newStartVnode,448          insertedVnodeQueue,449          newCh,450          newStartIdx451        )452        oldStartVnode = oldCh[++oldStartIdx]453        newStartVnode = newCh[++newStartIdx]454      } else if (sameVnode(oldEndVnode, newEndVnode)) {455        patchVnode(456          oldEndVnode,457          newEndVnode,458          insertedVnodeQueue,459          newCh,460          newEndIdx461        )462        oldEndVnode = oldCh[--oldEndIdx]463        newEndVnode = newCh[--newEndIdx]464      } else if (sameVnode(oldStartVnode, newEndVnode)) {465        // Vnode moved right466        patchVnode(467          oldStartVnode,468          newEndVnode,469          insertedVnodeQueue,470          newCh,471          newEndIdx472        )473        canMove &&474          nodeOps.insertBefore(475            parentElm,476            oldStartVnode.elm,477            nodeOps.nextSibling(oldEndVnode.elm)478          )479        oldStartVnode = oldCh[++oldStartIdx]480        newEndVnode = newCh[--newEndIdx]481      } else if (sameVnode(oldEndVnode, newStartVnode)) {482        // Vnode moved left483        patchVnode(484          oldEndVnode,485          newStartVnode,486          insertedVnodeQueue,487          newCh,488          newStartIdx489        )490        canMove &&491          nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)492        oldEndVnode = oldCh[--oldEndIdx]493        newStartVnode = newCh[++newStartIdx]494      } else {495        if (isUndef(oldKeyToIdx))496          oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)497        idxInOld = isDef(newStartVnode.key)498          ? oldKeyToIdx[newStartVnode.key]499          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)500        if (isUndef(idxInOld)) {501          // New element502          createElm(503            newStartVnode,504            insertedVnodeQueue,505            parentElm,506            oldStartVnode.elm,507            false,508            newCh,509            newStartIdx510          )511        } else {512          vnodeToMove = oldCh[idxInOld]513          if (sameVnode(vnodeToMove, newStartVnode)) {514            patchVnode(515              vnodeToMove,516              newStartVnode,517              insertedVnodeQueue,518              newCh,519              newStartIdx520            )521            oldCh[idxInOld] = undefined522            canMove &&523              nodeOps.insertBefore(524                parentElm,525                vnodeToMove.elm,526                oldStartVnode.elm527              )528          } else {529            // same key but different element. treat as new element530            createElm(531              newStartVnode,532              insertedVnodeQueue,533              parentElm,534              oldStartVnode.elm,535              false,536              newCh,537              newStartIdx538            )539          }540        }541        newStartVnode = newCh[++newStartIdx]542      }543    }544    if (oldStartIdx > oldEndIdx) {545      refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm546      addVnodes(547        parentElm,548        refElm,549        newCh,550        newStartIdx,551        newEndIdx,552        insertedVnodeQueue553      )554    } else if (newStartIdx > newEndIdx) {555      removeVnodes(oldCh, oldStartIdx, oldEndIdx)556    }557  }558559  function checkDuplicateKeys(children) {560    const seenKeys = {}561    for (let i = 0; i < children.length; i++) {562      const vnode = children[i]563      const key = vnode.key564      if (isDef(key)) {565        if (seenKeys[key]) {566          warn(567            `Duplicate keys detected: '${key}'. This may cause an update error.`,568            vnode.context569          )570        } else {571          seenKeys[key] = true572        }573      }574    }575  }576577  function findIdxInOld(node, oldCh, start, end) {578    for (let i = start; i < end; i++) {579      const c = oldCh[i]580      if (isDef(c) && sameVnode(node, c)) return i581    }582  }583584  function patchVnode(585    oldVnode,586    vnode,587    insertedVnodeQueue,588    ownerArray,589    index,590    removeOnly?: any591  ) {592    if (oldVnode === vnode) {593      return594    }595596    if (isDef(vnode.elm) && isDef(ownerArray)) {597      // clone reused vnode598      vnode = ownerArray[index] = cloneVNode(vnode)599    }600601    const elm = (vnode.elm = oldVnode.elm)602603    if (isTrue(oldVnode.isAsyncPlaceholder)) {604      if (isDef(vnode.asyncFactory.resolved)) {605        hydrate(oldVnode.elm, vnode, insertedVnodeQueue)606      } else {607        vnode.isAsyncPlaceholder = true608      }609      return610    }611612    // reuse element for static trees.613    // note we only do this if the vnode is cloned -614    // if the new node is not cloned it means the render functions have been615    // reset by the hot-reload-api and we need to do a proper re-render.616    if (617      isTrue(vnode.isStatic) &&618      isTrue(oldVnode.isStatic) &&619      vnode.key === oldVnode.key &&620      (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))621    ) {622      vnode.componentInstance = oldVnode.componentInstance623      return624    }625626    let i627    const data = vnode.data628    if (isDef(data) && isDef((i = data.hook)) && isDef((i = i.prepatch))) {629      i(oldVnode, vnode)630    }631632    const oldCh = oldVnode.children633    const ch = vnode.children634    if (isDef(data) && isPatchable(vnode)) {635      for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)636      if (isDef((i = data.hook)) && isDef((i = i.update))) i(oldVnode, vnode)637    }638    if (isUndef(vnode.text)) {639      if (isDef(oldCh) && isDef(ch)) {640        if (oldCh !== ch)641          updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)642      } else if (isDef(ch)) {643        if (__DEV__) {644          checkDuplicateKeys(ch)645        }646        if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')647        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)648      } else if (isDef(oldCh)) {649        removeVnodes(oldCh, 0, oldCh.length - 1)650      } else if (isDef(oldVnode.text)) {651        nodeOps.setTextContent(elm, '')652      }653    } else if (oldVnode.text !== vnode.text) {654      nodeOps.setTextContent(elm, vnode.text)655    }656    if (isDef(data)) {657      if (isDef((i = data.hook)) && isDef((i = i.postpatch))) i(oldVnode, vnode)658    }659  }660661  function invokeInsertHook(vnode, queue, initial) {662    // delay insert hooks for component root nodes, invoke them after the663    // element is really inserted664    if (isTrue(initial) && isDef(vnode.parent)) {665      vnode.parent.data.pendingInsert = queue666    } else {667      for (let i = 0; i < queue.length; ++i) {668        queue[i].data.hook.insert(queue[i])669      }670    }671  }672673  let hydrationBailed = false674  // list of modules that can skip create hook during hydration because they675  // are already rendered on the client or has no need for initialization676  // Note: style is excluded because it relies on initial clone for future677  // deep updates (#7063).678  const isRenderedModule = makeMap('attrs,class,staticClass,staticStyle,key')679680  // Note: this is a browser-only function so we can assume elms are DOM nodes.681  function hydrate(elm, vnode, insertedVnodeQueue, inVPre?: boolean) {682    let i683    const { tag, data, children } = vnode684    inVPre = inVPre || (data && data.pre)685    vnode.elm = elm686687    if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {688      vnode.isAsyncPlaceholder = true689      return true690    }691    // assert node match692    if (__DEV__) {693      if (!assertNodeMatch(elm, vnode, inVPre)) {694        return false695      }696    }697    if (isDef(data)) {698      if (isDef((i = data.hook)) && isDef((i = i.init)))699        i(vnode, true /* hydrating */)700      if (isDef((i = vnode.componentInstance))) {701        // child component. it should have hydrated its own tree.702        initComponent(vnode, insertedVnodeQueue)703        return true704      }705    }706    if (isDef(tag)) {707      if (isDef(children)) {708        // empty element, allow client to pick up and populate children709        if (!elm.hasChildNodes()) {710          createChildren(vnode, children, insertedVnodeQueue)711        } else {712          // v-html and domProps: innerHTML713          if (714            isDef((i = data)) &&715            isDef((i = i.domProps)) &&716            isDef((i = i.innerHTML))717          ) {718            if (i !== elm.innerHTML) {719              /* istanbul ignore if */720              if (721                __DEV__ &&722                typeof console !== 'undefined' &&723                !hydrationBailed724              ) {725                hydrationBailed = true726                console.warn('Parent: ', elm)727                console.warn('server innerHTML: ', i)728                console.warn('client innerHTML: ', elm.innerHTML)729              }730              return false731            }732          } else {733            // iterate and compare children lists734            let childrenMatch = true735            let childNode = elm.firstChild736            for (let i = 0; i < children.length; i++) {737              if (738                !childNode ||739                !hydrate(childNode, children[i], insertedVnodeQueue, inVPre)740              ) {741                childrenMatch = false742                break743              }744              childNode = childNode.nextSibling745            }746            // if childNode is not null, it means the actual childNodes list is747            // longer than the virtual children list.748            if (!childrenMatch || childNode) {749              /* istanbul ignore if */750              if (751                __DEV__ &&752                typeof console !== 'undefined' &&753                !hydrationBailed754              ) {755                hydrationBailed = true756                console.warn('Parent: ', elm)757                console.warn(758                  'Mismatching childNodes vs. VNodes: ',759                  elm.childNodes,760                  children761                )762              }763              return false764            }765          }766        }767      }768      if (isDef(data)) {769        let fullInvoke = false770        for (const key in data) {771          if (!isRenderedModule(key)) {772            fullInvoke = true773            invokeCreateHooks(vnode, insertedVnodeQueue)774            break775          }776        }777        if (!fullInvoke && data['class']) {778          // ensure collecting deps for deep class bindings for future updates779          traverse(data['class'])780        }781      }782    } else if (elm.data !== vnode.text) {783      elm.data = vnode.text784    }785    return true786  }787788  function assertNodeMatch(node, vnode, inVPre) {789    if (isDef(vnode.tag)) {790      return (791        vnode.tag.indexOf('vue-component') === 0 ||792        (!isUnknownElement(vnode, inVPre) &&793          vnode.tag.toLowerCase() ===794            (node.tagName && node.tagName.toLowerCase()))795      )796    } else {797      return node.nodeType === (vnode.isComment ? 8 : 3)798    }799  }800801  return function patch(oldVnode, vnode, hydrating, removeOnly) {802    if (isUndef(vnode)) {803      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)804      return805    }806807    let isInitialPatch = false808    const insertedVnodeQueue: any[] = []809810    if (isUndef(oldVnode)) {811      // empty mount (likely as component), create new root element812      isInitialPatch = true813      createElm(vnode, insertedVnodeQueue)814    } else {815      const isRealElement = isDef(oldVnode.nodeType)816      if (!isRealElement && sameVnode(oldVnode, vnode)) {817        // patch existing root node818        patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)819      } else {820        if (isRealElement) {821          // mounting to a real element822          // check if this is server-rendered content and if we can perform823          // a successful hydration.824          if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {825            oldVnode.removeAttribute(SSR_ATTR)826            hydrating = true827          }828          if (isTrue(hydrating)) {829            if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {830              invokeInsertHook(vnode, insertedVnodeQueue, true)831              return oldVnode832            } else if (__DEV__) {833              warn(834                'The client-side rendered virtual DOM tree is not matching ' +835                  'server-rendered content. This is likely caused by incorrect ' +836                  'HTML markup, for example nesting block-level elements inside ' +837                  '<p>, or missing <tbody>. Bailing hydration and performing ' +838                  'full client-side render.'839              )840            }841          }842          // either not server-rendered, or hydration failed.843          // create an empty node and replace it844          oldVnode = emptyNodeAt(oldVnode)845        }846847        // replacing existing element848        const oldElm = oldVnode.elm849        const parentElm = nodeOps.parentNode(oldElm)850851        // create new node852        createElm(853          vnode,854          insertedVnodeQueue,855          // extremely rare edge case: do not insert if old element is in a856          // leaving transition. Only happens when combining transition +857          // keep-alive + HOCs. (#4590)858          oldElm._leaveCb ? null : parentElm,859          nodeOps.nextSibling(oldElm)860        )861862        // update parent placeholder node element, recursively863        if (isDef(vnode.parent)) {864          let ancestor = vnode.parent865          const patchable = isPatchable(vnode)866          while (ancestor) {867            for (let i = 0; i < cbs.destroy.length; ++i) {868              cbs.destroy[i](ancestor)869            }870            ancestor.elm = vnode.elm871            if (patchable) {872              for (let i = 0; i < cbs.create.length; ++i) {873                cbs.create[i](emptyNode, ancestor)874              }875              // #6513876              // invoke insert hooks that may have been merged by create hooks.877              // e.g. for directives that uses the "inserted" hook.878              const insert = ancestor.data.hook.insert879              if (insert.merged) {880                // start at index 1 to avoid re-invoking component mounted hook881                // clone insert hooks to avoid being mutated during iteration.882                // e.g. for customed directives under transition group.883                const cloned = insert.fns.slice(1)884                for (let i = 0; i < cloned.length; i++) {885                  cloned[i]()886                }887              }888            } else {889              registerRef(ancestor)890            }891            ancestor = ancestor.parent892          }893        }894895        // destroy old node896        if (isDef(parentElm)) {897          removeVnodes([oldVnode], 0, 0)898        } else if (isDef(oldVnode.tag)) {899          invokeDestroyHook(oldVnode)900        }901      }902    }903904    invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)905    return vnode.elm906  }907}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.