preface

The initial rendering of original tags, custom components and slots was completed in the previous article. Of course, the principle of V-bind, V-model and V-ON instructions were also involved. After the first rendering, it’s time for the next update:

Reactive data is updated -> setter intercepts the update operation -> DEP tells Watcher to execute update method -> execute updateComponent method to update component -> execute render to generate a new VNode -> will Pass vnode to vm._update method -> call patch method -> execute patchVnode DOM diff operation -> complete update

The target

So, the goal of this article is to implement DOM Diff for subsequent updates. There’s only one thing involved: DOM Diff.

implementation

The next step is to implement DOM Diff for subsequent updates of reactive data.

patch

/src/compiler/patch.js

/** * Is responsible for the first rendering of the component and subsequent updates *@param {VNode} OldVnode oldVnode *@param {VNode} Vnode New vnode */
export default function patch(oldVnode, vnode) {
  if(oldVnode && ! vnode) {// If the old node exists and the new node does not exist, the component is destroyed
    return
  }

  if(! oldVnode) {// oldVnode does not exist, indicating that the child is rendering for the first time
  } else {
    if (oldVnode.nodeType) { // real node, which represents the first rendering of the root component
    } else {
      // Subsequent updates
      patchVnode(oldVnode, vnode)
    }
  }
}

Copy the code

patchVnode

/src/compiler/patch.js

/** * compare the old and new nodes, find the differences, and update the old node *@param {*} OldVnode Vnode * of the old node@param {*} Vnode Vnode */ of the new node
function patchVnode(oldVnode, vnode) {
  // If the new and old nodes are the same, the node ends
  if (oldVnode === vnode) return

  // Synchronize the real node on the old vnode to the new vnode; otherwise, vnode.elm will be empty during subsequent updates
  vnode.elm = oldVnode.elm

  // The new node is different from the old node
  const ch = vnode.children
  const oldCh = oldVnode.children

  if(! vnode.text) {// The new node does not have a text node
    if (ch && oldCh) { // Indicates that both the old and new nodes have children
      // diff
      updateChildren(ch, oldCh)
    } else if (ch) { // The old node has no children, the new node has children
      // Add child node
    } else { // The new node has no children, while the old node has children
      // Delete these child nodes}}else { // The new node has a text node
    if (vnode.text.expression) { // Indicates that an expression exists
      // Get the new value of the expression
      const value = JSON.stringify(vnode.context[vnode.text.expression])
      / / the old value
      try {
        const oldValue = oldVnode.elm.textContent
        if(value ! == oldValue) {// If the old and new values are different, update them
          oldVnode.elm.textContent = value
        }
      } catch {
        // Prevent an error when encountering a slot during update
        // Currently, reactive updates of slot data are not handled}}}}Copy the code

updateChildren

/src/compiler/patch.js

/** * diff, compare the child node, find the differences, and then update the differences to the old node *@param {*} Ch All child nodes of the new VNode *@param {*} OldCh all child nodes of old vNodes */
function updateChildren(ch, oldCh) {
  // Four cursors
  // The start index of the new child node is called new Start
  let newStartIdx = 0
  / / new end
  let newEndIdx = ch.length - 1
  / / old began
  let oldStartIdx = 0
  / / end of the old
  let oldEndIdx = oldCh.length - 1
  // Loop through the old and new nodes to find the differences between the nodes, and then update
  while (newStartIdx <= newEndIdx && oldStartIdx <= oldEndIdx) { // Root makes four assumptions for web DOM operations to reduce time complexity
    // New start node
    const newStartNode = ch[newStartIdx]
    // New end node
    const newEndNode = ch[newEndIdx]
    // Old start node
    const oldStartNode = oldCh[oldStartIdx]
    // Old end node
    const oldEndNode = oldCh[oldEndIdx]
    if (sameVNode(newStartNode, oldStartNode)) { // Assume that the new start and the old start are the same node
      // Compare the two nodes, find the differences and update
      patchVnode(oldStartNode, newStartNode)
      // Move the cursor
      oldStartIdx++
      newStartIdx++
    } else if (sameVNode(newStartNode, oldEndNode)) { // Assume that the new start and the old end are the same node
      patchVnode(oldEndNode, newStartNode)
      // Move the old end to the new start
      oldEndNode.elm.parentNode.insertBefore(oldEndNode.elm, oldCh[newStartIdx].elm)
      // Move the cursor
      newStartIdx++
      oldEndIdx--
    } else if (sameVNode(newEndNode, oldStartNode)) { // Assume that the new end and the old start are the same node
      patchVnode(oldStartNode, newEndNode)
      // Move the old start to the new end position
      oldStartNode.elm.parentNode.insertBefore(oldStartNode.elm, oldCh[newEndIdx].elm.nextSibling)
      // Move the cursor
      newEndIdx--
      oldStartIdx++
    } else if (sameVNode(newEndNode, oldEndNode)) { // Assume that the new end and the old end are the same node
      patchVnode(oldEndNode, newEndNode)
      // Move the cursor
      newEndIdx--
      oldEndIdx--
    } else {
      // If none of the above assumptions are true, then the same element can be found}}// Exit the loop, indicating that one of the nodes was traversed first
  if (newStartIdx < newEndIdx) { If the old node is traversed first, the remaining new nodes are added to the DOM

  }
  if (oldStartIdx < oldEndIdx) { // The remaining old nodes are deleted from the DOM after the new node is traversed}}Copy the code

sameVNode

/src/compiler/patch.js

/** * check whether two nodes are the same */
function sameVNode(n1, n2) {
  return n1.key == n2.key && n1.tag === n2.tag
}

Copy the code

The results of

Well, at this point, the diff process for the virtual DOM is complete, and if you can see the following renderings, everything is fine.

As you can see, the initial rendering and subsequent updates of the responsive data are complete. For Computed properties, it still doesn’t show up correctly, which is normal because it hasn’t been implemented yet, so I’m going to implement Conputed Computed properties, which is Computed in the next article, handwriting Vue2 series.

Focus on

Welcome everyone to pay attention to my nuggets account and B station, if the content to help you, welcome everyone to like, collect + attention

link

  • Proficient in Vue stack source code principles

  • Form a complete set of video

  • Learning exchange group