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