When application state changes, replacing the entire DOM tree does more work than necessary. `patchDOM(oldVdom, newVdom, parentEl)` compares the previously rendered virtual DOM with the new one, applies changes beneath the parent element, and returns the new virtual DOM. Initial rendering uses `mountDOM()`; later renders use `patchDOM()` instead of destroying and remounting everything. section 7.3 section 8.0
The function handles a few cases in order:
- If the top virtual nodes are not equal, find the old DOM node’s position, destroy its subtree, and mount the new subtree there. For a fragment whose `el` refers to the parent, a missing position becomes `null`, so mounting appends it. section 8.2.3
- Otherwise, copy `oldVdom.el` to `newVdom.el` to retain the DOM reference. For text nodes, change `el.nodeValue` only if the text differs. section 8.2.4
- For nodes with children, compare the old and new child arrays using `arraysDiffSequence()`. Flatten fragment children first; then mount additions, destroy removals, move nodes with `insertBefore()`, and recursively patch moved or unchanged children. section 8.2.6
A small update
Suppose a new `<span>` appears between two `<p>` elements. Mount the span at that position; the second paragraph shifts right as a result, without a separate move operation. If that paragraph’s classes and text also change, update those on its existing DOM node. section 7.2.2
Listing 8.7 Patching the DOM when the top nodes are different (patch-dom.js)
import { destroyDOM } from './destroy-dom'
import { mountDOM } from './mount-dom'
import { areNodesEqual } from './nodes-equal'
export function patchDOM(oldVdom, newVdom, parentEl) {
if (!areNodesEqual(oldVdom, newVdom)) {
const index = findIndexInParent(parentEl, oldVdom.el) #1
destroyDOM(oldVdom) #2
mountDOM(newVdom, parentEl, index) #3
return newVdom
}
}
function findIndexInParent(parentEl, el) {
const index = Array.from(parentEl.childNodes).indexOf(el)
if (index < 0) {
return null
}
return index
}
The first implementation case shows `areNodesEqual()` deciding whether to replace a subtree and `patchDOM()` returning the newly mounted virtual node. section 8.2.3
This deliberately simple replacement rule can rebuild an unchanged subtree when its parent changes. In return, ordinary updates can keep existing elements: a TODO input, for example, need not be replaced on every keystroke, so it keeps focus. section 8.2.3 section 8.0
