Blog>
Snippets

VNode patching and keyed updates

Shows how Vue's reactivity system patches VNodes optimally when data changes and the importance of keys for preserving state across re-renders.
function patchVNode(oldVNode, newVNode) {
  // Check if the same VNode type
  if (oldVNode.tag === newVNode.tag) {
    // Update the properties of the element
    const el = newVNode.el = oldVNode.el;
    updateProperties(el, oldVNode.data, newVNode.data);

    // Update children
    const oldChildren = oldVNode.children;
    const newChildren = newVNode.children;
    if (oldChildren && newChildren) {
      updateChildren(el, oldChildren, newChildren);
    } else if (newChildren) {
      // No old children, add all new children
      addVNodes(el, null, newChildren, 0, newChildren.length - 1);
    } else if (oldChildren) {
      // No new children, remove all old children
      removeVNodes(el, oldChildren, 0, oldChildren.length - 1);
    }
  } else {
    // Replace the entire node
    const el = createElm(newVNode);
    oldVNode.el.parentNode.replaceChild(el, oldVNode.el);
  }
}
This function defines the VNode patching logic, updating properties for a node if it's the same type, otherwise creating and replacing the node entirely. It's the core logic for efficiently updating the DOM in response to data changes.
function updateChildren(parentElm, oldCh, newCh) {
  let oldStartIdx = 0, newStartIdx = 0;
  let oldEndIdx = oldCh.length - 1, newEndIdx = newCh.length - 1;
  let oldStartVnode = oldCh[0], newStartVnode = newCh[0];
  let oldEndVnode = oldCh[oldEndIdx], newEndVnode = newCh[newEndIdx];
  let oldKeyToIdx, idxInOld, vnodeToMove;

  // Efficiently update and reorder children with keys
  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    // ...diffing and patching logic using keys to determine optimal updates...
  }
  // ...additional logic for handling other edge cases...
}
This function updates children of a VNode by comparing the old and new children nodes with each other. Keys are used to match nodes and apply minimal DOM operations, greatly improving performance and ensuring correct element identity.
function updateProperties(elm, oldProps, newProps) {
  for (let key in updateProps) {
    // Update property if it's changed
    if (oldProps[key] !== newProps[key]) {
      elm[key] = newProps[key];
    }
  }
}
This function applies property updates from the new VNode to the corresponding DOM element by directly setting the properties that have changed.
<template>
  <div>
    <ul>
      <li v-for="item in items" :key="item.id">
        {{ item.text }}
      </li>
    </ul>
  </div>
</template>
This Vue template uses the `v-for` directive with the `key` attribute to render a list. The key helps Vue's reactivity system track which items have changed, have been added, or have been removed, optimizing DOM updates.
new Vue({
  el: '#app',
  data: {
    items: [
      { id: 1, text: 'Item 1' },
      { id: 2, text: 'Item 2' }
      // More items...
    ]
  },
  methods: {
    addItem() {
      let newItemId = this.items.length + 1;
      this.items.push({ id: newItemId, text: 'Item ' + newItemId });
    },
    removeItem(itemId) {
      this.items = this.items.filter(item => item.id !== itemId);
    }
  }
});
This Vue instance showcases an interactive list managed by Vue's reactivity system. Methods to add or remove items demonstrate keyed updates as Vue intelligently patches the DOM only where necessary, preserving state and avoiding unnecessary re-renders.