1. What is patch? Briefly what does Patch do?
As we have learned above, in order to save frequent DOM operation, we set up a vnode virtual node. According to the differences between the old and new nodes, we operate the real DOM. And this process of comparison, we call patch.
var patch = createPatchFunction();
Vue.prototype.__patch__ = patch
Copy the code
What did Patch do?
- Create new nodes (element nodes, comment nodes, text nodes)
- Example Delete a node that does not exist
- Modify the nodes to be updated
2. Patch process
3. Add a node
if (isUndef(oldVnode)) { // If oldValue is empty
isInitialPatch = true; createElm(vnode, insertedVnodeQueue, parentElm, refElm); } For the distinction of three types of nodes: element node must have a tag, comment node isComment must betrueIf neither is satisfied, it is a text nodeif (isDef(tag)) {}
else if (isTrue(vnode.isComment)){}
else{}
Copy the code
4. Delete the node
function removeVnodes (vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
var ch = vnodes[startIdx];
if (isDef(ch)) {
if (isDef(ch.tag)) {
removeAndInvokeRemoveHook(ch); // Remove the DOM and execute the remove hook function
invokeDestroyHook(ch); // Execute the Module deStory hook function and vNode deStory hook function
} else { // Text noderemoveNode(ch.elm); }}}}Copy the code
5. Update the node
1.Static nodes will skip the update2.If the new node has a text property and is not equal to the old node's text, then call setTextContent directly to change the DOM node's content to the text saved by the text property, no matter what the previous child node was3.Determine if the new node has children, if no children. Then simply delete the childern of the old node. If there are children, we need to make a more detailed comparison of children, including deletion and addition. Moving position, etcCopy the code
6. The implementation strategy of DIFF is the updateChildren method
- New front: the first of all unprocessed nodes in newChildren
- New empress: The last node in newChildren that has not been processed
- Old before: the first node of all unprocessed in oldChildren
- Old empress: The last node in oldChildren that has not been processed
Compare update steps
Before new and before old
sameVnode(oldStartVnode, newStartVnode)
New tail and old tail
sameVnode(oldStartVnode, newStartVnode)
New tail and old head
sameVnode(oldStartVnode, newEndVnode)
If the nodes are the same then you need to move them. Source code is:
nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));
function insertBefore (parentNode, newNode, referenceNode) { parentNode.insertBefore(newNode, referenceNode); } the insertBefore function inserts at the end of the current parent if the referenceNode is nullCopy the code
New head and old tail
sameVnode(oldEndVnode, newStartVnode) / / to compare
nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm) / / insert
Copy the code
Create an index to traverse the query
CreateKeyToOldIdx && findIdxInOld iterates to see if the new node can be found in the old node list. If not, create it directly. If it can be found, compare whether it is the same node, and move it to the front of the oldStartVnode node. If not, create it directly before oldStartVnodeCopy the code
Process the remaining nodes
if (oldStartIdx > oldEndIdx) {
refElm = isUndef(newCh[newEndIdx + 1])?null : newCh[newEndIdx + 1].elm;
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
} else if (newStartIdx > newEndIdx) {
removeVnodes(oldCh, oldStartIdx, oldEndIdx);
}
Copy the code
Attach the source code
function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
var oldStartIdx = 0;
var newStartIdx = 0;
var oldEndIdx = oldCh.length - 1;
var oldStartVnode = oldCh[0];
var oldEndVnode = oldCh[oldEndIdx];
var newEndIdx = newCh.length - 1;
var newStartVnode = newCh[0];
var newEndVnode = newCh[newEndIdx];
var oldKeyToIdx, idxInOld, vnodeToMove, refElm;
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVnode)) { // Determines if it exists because each time a node is matched, the node is set to undefined or false
oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
} else if (isUndef(oldEndVnode)) {
oldEndVnode = oldCh[--oldEndIdx];
} else if (sameVnode(oldStartVnode, newStartVnode)) { // Old head, new head
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
} else if (sameVnode(oldEndVnode, newEndVnode)) { // Old tail and new tail
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldStartVnode, newEndVnode)) { // Old head and new tail
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldEndVnode, newStartVnode)) { // Old tail and new head
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
} else {
if (isUndef(oldKeyToIdx)) { // Create the serial number of the old node
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
if (isUndef(idxInOld)) { // The new node cannot be found in the old node
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
} else {
vnodeToMove = oldCh[idxInOld];
if (sameVnode(vnodeToMove, newStartVnode)) {
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue);
oldCh[idxInOld] = undefined;
canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);
} else {
// same key but different element. treat as new element
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx); } } newStartVnode = newCh[++newStartIdx]; }}if (oldStartIdx > oldEndIdx) {
refElm = isUndef(newCh[newEndIdx + 1])?null : newCh[newEndIdx + 1].elm;
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
} else if(newStartIdx > newEndIdx) { removeVnodes(oldCh, oldStartIdx, oldEndIdx); }}Copy the code