Like React, Vue has its own diff algorithm, Patch, to handle updates to the virtual DOM.

What is a patch

When Vue renders DOM through VNode node, it does not update DOM node violently through the current VNode node. Instead, it compares the old and new VNode nodes through patch algorithm, and then finds out the different attributes or nodes according to needs. Obviously, Patch can reduce unnecessary overhead and improve performance.

In the process of patch, the following things are mainly accomplished:

  • Create a node to be added
  • Remove an obsolete node
  • Move or modify nodes that need to be updated

Patch optimization of Vue3 — patchFlag

The optimization of patchFlag for patch has been mentioned in many speeches of Vue3 of UVU, so I paid special attention to the part of patchFlag when reading the source code. So what is the use of patchFlag?

For example, when two nodes are of the same type, although the two nodes are of the same type, the attributes of the new node may also change, so we need to traverse the attributes of the node to effectively judge whether the update is needed.

Let’s look at the following elements:

<div id="bar" :class="foo">Hello World</div>
Copy the code

For such a div element, it is clear from the front end engineer’s point of view that id=”bar” and children are text types that are static and will not change, just to see if the class changes. However, Vue3 now achieves this by using patchFlag. After generating AST tree, when traversing each node through converter, corresponding patchFlag will be added according to the characteristics of the node. In the process of patch, only the props (class) is processed, but not the full comparison. This reduces the number of times you need to traverse the props, thereby improving performance.

How does Patch work

After explaining the role of patch, I will take you to see the source code implementation of patch. The source code of Patch is located in the @runtime-core/ SRC /renderer.ts file. Because the code in this file is very long, the non-essential code will be omitted when explaining the code, and only the main logic will be introduced.

Because I hope that what you can gain from reading this article is the logic summary in the patch process and pay attention to the key logic. Not everyone needs to read the source code, and some of the key code is posted in the hope that students interested in reading the source code can have a code snippet reference.

const patch: PatchFn = (
  n1,
  n2,
  container,
  anchor = null,
  parentComponent = null,
  parentSuspense = null,
  isSVG = false,
  slotScopeIds = null,
  optimized = false
) = > {
  // Patching & vNodes that are not of the same type are uninstalled from the node tree
  if(n1 && ! isSameVNodeType(n1, n2)) { anchor = getNextHostNode(n1) unmount(n1, parentComponent, parentSuspense,true)
    n1 = null
  }
	// PatchFlag is the BAIL type
  if (n2.patchFlag === PatchFlags.BAIL) {
    optimized = false
    n2.dynamicChildren = null
  }

  const { type, ref, shapeFlag } = n2
  switch (type) { // Determine according to the Vnode type
    case Text: // Text type
      processText(n1, n2, container, anchor)
      break
    case Comment: // Comment type
      processCommentNode(n1, n2, container, anchor)
      break
    case Static: // Static node type
      if (n1 == null) {
        mountStaticNode(n2, container, anchor, isSVG)
      }
      break
    case Fragment: / / type of fragments
      processFragment(/* Ignore the argument */)
      break
    default:
      if (shapeFlag & ShapeFlags.ELEMENT) { // Element type
        processElement(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
      } else if (shapeFlag & ShapeFlags.COMPONENT) { // Component type
        processComponent(/* Ignore the argument */)}else if (shapeFlag & ShapeFlags.TELEPORT) { / / TELEPORT type; (type as typeof TeleportImpl).process(/* Ignore the argument */)}}}Copy the code

N1 and n2 are the two nodes to be compared. N1 is the old node, and n2 is the new node. Container is the container of the new node, while anchor is an anchor point, which is used to mark the node as the reference when we add, delete or move the old and new nodes. Optimized parameter Indicates whether to enable optimization mode. Other parameters are not introduced, we don’t care about for the moment.

Let’s look at the first if condition, which unloads the old node from the node tree when the old node exists and the new node is not of the same type. This is the first logic of patch that can be summarized: when the types of two nodes are different, the old node can be directly uninstalled.

Then look at the second if branch condition. If the patchFlag value of the new node is BAIL, the optimization mode will be closed. This is the first time we have encountered patchFlag in the source code, but don’t go into too much detail here, let’s move on.

Then the Patch function will judge the node type through the switch case and perform different operations for different node types.

Let’s take the ELEMENT type for example, because HTML elements are the type we spend most of our time with. In the source code above, when case goes to the default branch, At this point, the current VNode is judged to be an element type by the bitpress and operation of shapeFlag. Then processElement is called to continue the patch process and the parameters in the patch function are passed. I’m just going to follow the processElement function.

Element Patch process – processElement

The code logic of processElement itself is very simple, which can be summed up in one sentence: if there is an old node, continue to compare the old and new nodes by patch; otherwise, mount the new node directly. The source code is as follows:

const processElement = (
  n1: VNode | null,
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean,
  slotScopeIds: string[] | null,
  optimized: boolean
) = > {
  // If the old node does not exist
  if (n1 == null) {
    mountElement(
      n2,
      container,
      anchor
      /* Omit */ for subsequent parameters)}else {
    patchElement(
      n1,
      n2,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized
    )
  }
}
Copy the code

When the old node does not exist, it is relatively easy to mount it directly. Therefore, in order to reflect the patch process, I will assume that both the old and new nodes exist and continue the analysis along the patchElement.

Compare the child node — patchElement

Vue3 first extracts the props declarations of the old and new nodes during the patch process of the element type, because the patches need to be compared later.

Some hooks are triggered before the comparison begins, such as VNode’s own hooks: onVnodeBeforeUpdate, and the hooks beforeUpdate of directives bound on the element.

if ((vnodeHook = newProps.onVnodeBeforeUpdate)) {
  invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
}
if (dirs) {
  invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')}Copy the code

Update the attributes

After that, the props are compared. If the element is marked with patchFlag, the comparison will be performed on demand through patchFlag. Otherwise, the props in the element will be diff.

if (patchFlag > 0) {
  if (patchFlag & PatchFlags.FULL_PROPS) {
    // If the element props contains dynamic keys, a full comparison is required
    patchProps(
      el,
      n2,
      oldProps,
      newProps,
      parentComponent,
      parentSuspense,
      isSVG
    )
  } else {
    if (patchFlag & PatchFlags.CLASS) {
      if(oldProps.class ! == newProps.class) { hostPatchProp(el,'class'.null, newProps.class, isSVG)
      }
    }

    if (patchFlag & PatchFlags.STYLE) {
      hostPatchProp(el, 'style', oldProps.style, newProps.style, isSVG)
    }

    if (patchFlag & PatchFlags.PROPS) {
      const propsToUpdate = n2.dynamicProps!
      for (let i = 0; i < propsToUpdate.length; i++) {
        const key = propsToUpdate[i]
        const prev = oldProps[key]
        const next = newProps[key]
        if( next ! == prev || (hostForcePatchProp && hostForcePatchProp(el, key)) ) { hostPatchProp( el, key, prev, next, isSVG, n1.childrenas VNode[],
            parentComponent,
            parentSuspense,
            unmountChildren
          )
        }
      }
    }
  }

  if (patchFlag & PatchFlags.TEXT) {
    if(n1.children ! == n2.children) { hostSetElementText(el, n2.childrenas string)}}}else if(! optimized && dynamicChildren ==null) {
  patchProps(
    el,
    n2,
    oldProps,
    newProps,
    parentComponent,
    parentSuspense,
    isSVG
  )
}

Copy the code

Let’s take a look at the upper branch condition and see what patchFlag does at this point.

  • If patchFlag is FULL_PROPS, it indicates that the element may contain dynamic keys and needs to perform full props diff.

  • When patchFlag is CLASS, when the classes of the new and old nodes are inconsistent, the CLASS will be patched. When the CLASS attributes of the new and old nodes are completely consistent, no operation is required. This Flag is added when an element has a dynamic class binding.

  • When patchFlag is a STYLE, the STYLE will be updated, which will be done every patch. This Flag will be added when there is a dynamic STYLE binding.

  • When patchFlag is PROPS, note that this Flag is added when the element has dynamic properties or attrs bindings. Unlike class and style, these dynamic prop or attrs keys are saved for faster iteration.

    • The comparison of PROPS extracts the dynamic properties of the new node and traverses all keys in the properties. When the new and old properties are inconsistent or the key needs to be updated forcibly, hostPatchProp is called to update the properties.
  • When patchFlag is TEXT, hostSetElementText will be called for updating if the TEXT of the child nodes in the old and new nodes changes. This flag is added when the child nodes of the element contain only dynamic text.

At this point, when the element has patchFlag, the branch judgment ends. We can feel patchFlag’s efforts to improve the speed of patch algorithm in these branch judgments.

Branch to the last else. If there is no optimization flag and the dynamic child node does not exist, the full diff is performed on the props, using the patchProps function.

For the comparison of props, we are going to leave it at this point due to space limitations and not expand into the specific function calls in the branch.

Update the child node — patchChildren

Next comes the most important part of updating the child node. During the patch process of elements, it will judge whether there are dynamic child nodes. If so, it will call patchBlockChildren to update only the dynamic child nodes; otherwise, it will call patchChildren to fully update the child nodes.

Here we will look at the general situation: patchChildren.

When updating the child nodes, the ability of patchFlag is first used to classify the child nodes and make different treatments. In order to avoid a large amount of source code pasting, I summarize the logic of patchChildren function here, and then we will focus on the common parts.

  • Determine according to patchFlag:
    • If patchFlag is KEYED_FRAGMENT with key value, call patchKeyedChildren to continue processing child nodes.
    • If patchFlag is Fragment: UNKEYED_FRAGMENT without key value, call patchUnkeyedChildren to process the child node without key value.
  • Judge by shapeFlag (element type flag) :
    • If the new child node is of text type and the old child node is of array type, the child node of the old node is unloaded directly.
      • If the old and new node types are the same, the text of the new child node is updated directly.
    • If the old child node type is an array type
      • If the new child node is also of array type, patchKeyedChildren is called for a full diff.
      • If the new child node is not of array type, there is no new child node and you can simply uninstall the old node from the tree.
    • If the old child node is a text type, you can be sure that the new child node is not a text type because you have already determined whether the new child node is a text type in the first place, and you can simply set the element’s text to an empty string.
    • If the type of the new child node is array but that of the old child node is not array, mount the new child node to the tree.

The above is the complete logic of patchChildren. The most complicated logic here is to call patchKeyedChildren when both the old and new child nodes are array types to make a complete comparison between the two arrays.

Update policy of the child node

How to do diFF algorithm efficiently, the most important performance bottleneck is how to compare the child nodes in the tree more quickly, and figure out what specific operations need to be done. If we compare according to normal thinking, then the time complexity is at least O(n^3), then the tree of 1000 child nodes need at least 1 billion comparisons. There is no doubt that this operation is very expensive. In this case, how to implement a time complexity O(n) algorithm is a problem that the front-end framework must consider. Like React, THERE are keys in Vue to help identify and compare child nodes more efficiently.

Vue2 child node optimization strategy

When I read the book Vue.js, the author Liu Bowen summed up the child node optimization strategies of Vue2 into four categories:

  • The old and the new
  • New queen and old queen
  • The new queen and the old one
  • New before and old after

It should be noted that the new before refers to the node whose new child node index is in the first place, while the new after refers to the node whose new child node index is in the last place, while the old refers to the meaning of the old child node.

After reading this book at that time, I felt that such a summary was very appropriate and easy to remember, and also helped me to read the source code of Vue2 more easily and easily. Therefore, I still plan to use such a name to describe the update strategy in Vue3 child node update strategy.

Vue3 child node updated

Next, I will divide the patchKeyedChildren function into logical points one by one, and paste the source code and corresponding pictures according to the rhythm we described.

const patchKeyedChildren = (
  c1: VNode[],
  c2: VNodeArrayChildren,
  container: RendererElement,
  parentAnchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean,
  slotScopeIds: string[] | null,
  optimized: boolean
) = > {
  let i = 0
  const l2 = c2.length
  let e1 = c1.length - 1 // prev ending index
  let e2 = l2 - 1 // next ending index
  /* Ignore subsequent logic */
}
Copy the code

Firstly, let’s look at the function signature part of patchKeyedChildren. C1 and c2 in the function parameters respectively represent the old child node and the new child node.

The function starts by declaring four variables:

  • Iterate over the index I = 0 of the child node
  • Length of new child node: L2
  • Last index of the old child node: E1
  • Last index of the new child node: e2

With these four declared variables in mind, we look at the first comparison strategy for word child nodes in Vue3.

The old and the new

To start with, C1 and C2 nodes in the same row are the two old and new child nodes to be compared. The child nodes are compared from the starting index of the two child nodes:

When I = 0, the 0th index is compared and it is found that A node of C1 and A node of C2 are elements of the same type, then patch operation will be performed on the old and new A nodes. Patch operation here can recursively access all child nodes under A node. After patch is completed, increase index I.

After continuous comparison, it is found that B node of C1 and B node of C2 are also elements of the same type. As before, patch recursion is performed on B node and I index is increased.

When the third child node is compared, it will be found that THE C node of C1 and the D node of C2 are not of the same type of node, so the comparison cycle between the new and the old will break out, and the comparison between the new and the old ends.

At this point, the first two nodes of C1 and C2’s children are compared. The comparison process in the source code looks like this:

while (i <= e1 && i <= e2) {
  const n1 = c1[i]
  const n2 = (c2[i] = optimized
    ? cloneIfMounted(c2[i] as VNode)
    : normalizeVNode(c2[i]))
  // Compare whether N1 and n2 are the same type of VNode
  if (isSameVNodeType(n1, n2)) {
    patch(
      n1,
      n2,
      container,
      null,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized
    )
  } else {
    // If n1 and n2 are not of the same type, break the while loop
    break
  }
  Increasing / / I
  i++
}
Copy the code

New queen and old queen

After the comparison between the new and the old is completed, in accordance with the optimization strategy in Vue2, the comparison between the new and the old will start.

Let’s take a look at the example pictures of the new and the old. I believe that after the introduction of the new and the old, we can already understand the comparison of the new and the old.

Compare elements from the very end through the trailing indexes of the old and new child nodes, E1 and E2, as previously declared.

According to the comparison in the figure, the logic is as follows:

  • From the end, C1 is the C node, and C2 is also the C node. The two nodes are of the same type, and the patch comparison starts. After the patch is completed, the end index of the old and new child nodes is -1.
  • For the second comparison, the end of C1 is node B, and the end of C2 is node B. The type is the same. Patch is performed, and then the tail index is decreased.
  • For the third comparison, the last node of C1 is A, and the last node of C2 is E. The type is different, and break breaks out of the comparison cycle of new and old.

The following is the new after the comparison with the old source code:

while (i <= e1 && i <= e2) {
  const n1 = c1[e1]
  const n2 = (c2[e2] = optimized
    ? cloneIfMounted(c2[e2] as VNode)
    : normalizeVNode(c2[e2]))
  // Compare whether N1 and n2 are the same type of VNode
  if (isSameVNodeType(n1, n2)) {
    patch(
      n1,
      n2,
      container,
      null,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized
    )
  } else {
    // If n1 and n2 are not of the same type, break the while loop
    break
  }
  // After the patch operation is complete, the tail index decreases
  e1--
  e2--
}
Copy the code

New child nodes in regular order are mounted

When we complete the comparison of the first two rounds, we often find some elements existing in the new child nodes in the conventional ordinal number, but not in the old child nodes. At this time, we need to insert these new child nodes.

As shown in the figure, when the comparison between the new and the old is completed, the index I has increased beyond the length of C1 child node, at this point I = 2, and I is less than or equal to the length of C2 child node, so it can be determined that there are nodes in the new child node that have not been traversed, and the old child node has been traversed completely. Therefore, insert all child nodes that have not been traversed.

For this diagram, the C node should be inserted.

The source code for this mount process is as follows, refer to the comments in the source code:

// When the old child node is traversed
if (i > e1) {
  // The new node still has elements that have not been traversed
  if (i <= e2) {
    const nextPos = e2 + 1
    // Define the anchor element
    const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor
    // Iterate over the remaining new child nodes
    while (i <= e2) {
    	// The first parameter of patch is passed null, indicating that there is no old node, and the new node can be directly inserted
      patch(
        null,
        (c2[i] = optimized
          ? cloneIfMounted(c2[i] as VNode)
          : normalizeVNode(c2[i])),
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
      i++
    }
  }
}
Copy the code

Remove redundant nodes in regular order

Corresponding to the mount logic above is the removal of redundant old child nodes.

When the new child node has been traversed, if there are still elements of the old child node that have not been traversed at this time, it can be determined that the remaining old child nodes are no longer needed, so the remaining old child nodes can be removed directly.

It can be seen from the figure that after the comparison between C1 and C2 is completed, there is still node A in the old child node C1, and there are no nodes to be compared in the new node, so node A can be removed directly.

The corresponding logic in the source code is also very simple:

// If the new child node has been traversed
else if (i > e2) {
  // The child node is not traversed
  while (i <= e1) {
    // Unmount the old child node
    unmount(c1[i], parentComponent, parentSuspense, true)
    // Increment index
    i++
  }
}
Copy the code

Child node comparison of unknown order

After the above ideal case processing is complete, we are left with scenarios where we cannot determine the location of elements.

Let’s take a look at this figure first. It can be seen that there are more elements mounted in both the old and new child nodes. After we compare the new and old child nodes and the new and old child nodes, there are still nodes of uncertain order in the middle part. CDE in the old child node, EDCH in the new child node.

As an observer, we know that the CDE needs to be moved and the H node needs to be inserted. Let’s take a look at how Patch accomplishes this:

  • Declare two variables s1 and s2, and assign the preorder index I traversed at this time to S1 and S2. S1 and S2 represent the starting index of the old and new child nodes respectively.

  • With S2 as the starting node and E2 as the ending condition, the new child node is traversed. With the key of the neutron node of the new child node as the key and index I as the value, a Map object is generated to store the original index.

    • Duplicate keys found during update XXX, Make sure keys are unique. Duplicate keys found during update XXX
    const s1 = i // Start index of the old child node
    const s2 = i // Start index of the new child node
    
    // Create an indexed map object for the new child node
    const keyToNewIndexMap: Map<string | number.number> = new Map(a)for (i = s2; i <= e2; i++) {
      const nextChild = (c2[i] = optimized
        ? cloneIfMounted(c2[i] as VNode)
        : normalizeVNode(c2[i]))
      if(nextChild.key ! =null) {
        // If the DEV environment is used and keyToNewIndexMap already has the key value of the current node, a warning is generated.
        if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {
          warn(
            `Duplicate keys found during update:`.JSON.stringify(nextChild.key),
            `Make sure keys are unique.`)}// Use the new child's key as the key and index as the value to store the map.
        keyToNewIndexMap.set(nextChild.key, i)
      }
    }
    Copy the code
  • Declare variable toBePatched, there are still several nodes that need toBePatched. Declare the variable patched = 0, and record the number of patch nodes.

  • Declare an array of newIndexToOldIndexMap for determining the longest increment subsequence. NewIndexToOldIndexMap array size is toBePatched length, and initialize all elements in the array to 0.

    • NewIndexToOldIndexMap, of the form Map
      ,>
    • Note that oldIndex is offset by +1.
    • OldIndex = 0 is a special value indicating that there is no corresponding old child in the new child node.
  • Iterate over the old child node, marking the currently traversed child as prevChild.

    • Patched: if patched is greater than or equal to toBePatched, all nodes toBePatched have been compared. You can remove remaining prevChild.
    • Otherwise declare the variable newIndex.
    • If prevChild’s key is not empty, take the prevChild.key value from keyToIndexMap and assign the obtained value to newIndex.
    • If newIndex does not have a value, there is no corresponding old child in the new child, so remove the prevChild.
    • Otherwise, store the new index in newIndexToOldIndexMap, mark the furthest position of the current index movement or add the movement mark, and compare the patch between the old and new child nodes.
    • After patch is patched, increment the patch count.

The code for the above logic is as follows:

/** * iterate over the old child nodes, try to patch the nodes that need to be patched, and remove the child nodes that will not appear again */
let j
let patched = 0
const toBePatched = e2 - s2 + 1
let moved = false
// Used to track whether any nodes have moved
let maxNewIndexSoFar = 0
// Used to determine the longest increasing subsequence
const newIndexToOldIndexMap = new Array(toBePatched)
for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0

for (i = s1; i <= e1; i++) {
  const prevChild = c1[i]
  if (patched >= toBePatched) {
    // All new nodes are patched, so the rest just need to be removed
    unmount(prevChild, parentComponent, parentSuspense, true)
    continue
  }
  let newIndex
  if(prevChild.key ! =null) {
    newIndex = keyToNewIndexMap.get(prevChild.key)
  } else {
    // Try to locate nodes of the same type for nodes where key cannot be found
		/* Ignore logic */
  }
  // If the old child node does not match the new child node, the old child node is removed
  if (newIndex === undefined) {
    unmount(prevChild, parentComponent, parentSuspense, true)}else {
    // Record the index of the patched node in newIndexToOldIndexMap
    newIndexToOldIndexMap[newIndex - s2] = i + 1
    // If the index of newIndex is greater than the farthest moved index, the index is updated
    if (newIndex >= maxNewIndexSoFar) {
      maxNewIndexSoFar = newIndex
    } else {
      // Otherwise mark moved to true
      moved = true
    }
    // Patch the old and new child nodes
    patch(
      prevChild,
      c2[newIndex] as VNode,
      container,
      null,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized
    )
    // After patch is complete, increment the number of patches.
    patched++
  }
}
Copy the code

In after traversing the old child nodes, we have removed the unnecessary old child nodes, and is still present in the new node to the corresponding old child nodes are compared, and the patch then we need to traverse the new child node, only from the back forward traversal needs to be part of the patch, the purpose is to insert for the new child node, Peer movement of the child nodes that need to be moved. The logical description is as follows:

  • If the moved tag is present, find the longest incrementing subsequence from newIndexToOldIndexMap and assign j to the end index of the array of longest incrementing subsequences.
  • Traversing the new child node from back to front allows us to determine the location of the anchor element.
  • Declare newIndex = s2 + I, that is, the last node to be patched.
  • Gets the anchor element.
  • If the node needs to be patched, the value of the I index in newIndexToOldIndexMap is 0. Remember I mentioned earlier that 0 is a special value that means the node has no corresponding node in the old child node. We insert elements that have no corresponding nodes.
  • If there is an index in newIndexToOldIndexMap but the moved mark exists, the node may have moved and you should continue to determine.
    • If j < 0, all nodes in the longest increasing subsequence have been processed. Or when index I is not equal to the value corresponding to index J in the longest growing subsequence, it indicates that the node is not in a relatively stable position, and the operation of moving is required.
    • If the above conditions are met, the j index decreases and the node is not processed.

The above logic code is as follows:

/** * move and mount */
// Create the longest increasing subsequence when the node is moved
const increasingNewIndexSequence = moved
  ? getSequence(newIndexToOldIndexMap)
  : EMPTY_ARR
j = increasingNewIndexSequence.length - 1
// In order to easily obtain the anchor point, choose to traverse from back to front
for (i = toBePatched - 1; i >= 0; i--) {
  const nextIndex = s2 + i
  const nextChild = c2[nextIndex] as VNode
  const anchor =
    nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor
  if (newIndexToOldIndexMap[i] === 0) {
		// If the corresponding index cannot be found in newIndexToOldIndexMap, a new node is added
    patch(
      null,
      nextChild,
      container,
      anchor,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized
    )
  } else if (moved) {
    If it is not a stable subsequence, or if the current node is not on an increasing subsequence, it needs to be moved
    if (j < 0|| i ! == increasingNewIndexSequence[j]) { move(nextChild, container, anchor, MoveType.REORDER) }else {
      j--
    }
  }
}
Copy the code

So far for the existence of the key value of the child node update is over, follow me to look down, if you can carefully combine the source code and logic together to see, then I believe that understanding the child node update source is a very easy thing.

conclusion

In this paper, I introduced the role of patch and the acceleration of Vue3’s performance optimization through patchFlag when updating nodes.

Then the patch function process is explained, and the ELEMENT type is taken as an example to follow the function call process down to the analysis, until the update of child nodes is introduced.

In the update of child nodes, we compared the different update strategies of Vue2 and Vue3, and explained in detail how Vue3 compared child nodes with key values.

Here I can summarize Vue3’s child node updates in five steps:

1. The comparison between the new and the old.

2. The comparison between the new and the old.

3. New operations for regular sequences.

4. Remove routine sequences.

5. Processing of unknown order.

5.1 Creating a map of keys and indexes for new child nodes: keyToNewIndexMap. 5.2 Traversing old child Nodes Patch the old nodes that can be matched and remove the old child nodes that do not exist. 5.3 Move or add a child node.Copy the code

The above 5 points are the very important child node update process in Vue3. If you feel that the article is too long to look at, you can also read the summary directly.

If this article can help you understand the patch process and child node update strategy in Vue3, I hope you can give this article a like ❤️. If you want to continue to follow the following articles, you can also follow my account, thank you again for reading so far.