The core use
- Solves row sorting for the most frequently updated tables
The key to improving performance
- Since dom creation is expensive, it’s better to use the original DOM if you can, using node.insertBefore () instead of createElement+replaceChild
Algorithm analysis
- First go order:
- oldStartVnode, newStartVnode
- oldEndVnode, newEndVnode
- oldStartVnode, newEndVnode
- oldEndVnode, newStartVnode
- Failing all of the above,
- FindIndex, walk through the Vnode to see if it is the same Vnode
- Although it is one by one traversal, but the speed of JS is very fast, compared to the consumption of dom operation, or very cheap
- So I’m going to look for the key first, and if I have the key, I’m going to use the map that I’m going to map with the key.
- Therefore, if there is frequent commutator, it is necessary to add a key, such as v-for, which can improve the performance of diff
- How it works: insertBefore definitely performs better than createElement+replaceChild
- FindIndex, walk through the Vnode to see if it is the same Vnode
- The following is the diff algorithm core source code, with personal comments
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;
// removeOnly is a special flag used only by <transition-group>
// to ensure removed elements stay in correct relative positions
// during leaving transitions
varcanMove = ! removeOnly; { checkDuplicateKeys(newCh); }while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVnode)) { // Make sure oldVnode exists (this function handles child, here it determines whether oldVnode has child)
oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
} else if (isUndef(oldEndVnode)) { // Make sure oldvNodes exist
oldEndVnode = oldCh[--oldEndIdx];
} else if (sameVnode(oldStartVnode, newStartVnode)) { // (compare from top to bottom) If the old vNode is the same as the new vnode, then run patchVnode recursively
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx); // After running a child, run the next child
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
} else if (sameVnode(oldEndVnode, newEndVnode)) { // (from bottom to top)
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldStartVnode, newEndVnode)) { // (The scenario table is reordered by the index in reverse order) Vnode Moved Right
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)); // Insert the oldNode DOM directly into the new ELM because of sameNode
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldEndVnode, newStartVnode)) { // (Reverse order) Vnode Moved left
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); // Insert the oldNode DOM directly into the new ELM because of sameNode
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
} else {
if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }
idxInOld = isDef(newStartVnode.key) // If there is a key in the patchVnode list, then run patchVnode again. If there is a key in the patchVnode list, run patchVnode again
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
if (isUndef(idxInOld)) { // New element
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
} else {
vnodeToMove = oldCh[idxInOld];
if (sameVnode(vnodeToMove, newStartVnode)) {
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
oldCh[idxInOld] = undefined;
canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm); // Insert the oldNode DOM directly into the new ELM because of sameNode
} 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) { // (after comparing sameVnode)newVnode is more than oldVnode
refElm = isUndef(newCh[newEndIdx + 1])?null : newCh[newEndIdx + 1].elm;
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
} else if (newStartIdx > newEndIdx) { If newVnode is less than oldVnode, delete some nodes from the original pageremoveVnodes(oldCh, oldStartIdx, oldEndIdx); }}function checkDuplicateKeys (children) {
var seenKeys = {};
for (var i = 0; i < children.length; i++) {
var vnode = children[i];
var key = vnode.key;
if (isDef(key)) {
if (seenKeys[key]) {
warn(
("Duplicate keys detected: '" + key + "'. This may cause an update error."),
vnode.context
);
} else {
seenKeys[key] = true; }}}}function sameVnode (a, b) { // Judge rule: 1. Key equal 2.... Check whether the two VNodes are equal
return (
a.key === b.key && (
(
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
) || (
isTrue(a.isAsyncPlaceholder) &&
a.asyncFactory === b.asyncFactory &&
isUndef(b.asyncFactory.error)
)
)
)
}
// 根据index判定, 如果新增node 则createElm
function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) {
for (; startIdx <= endIdx; ++startIdx) {
createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm, false, vnodes, startIdx); }}function removeVnodes (vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
var ch = vnodes[startIdx];
if (isDef(ch)) {
if (isDef(ch.tag)) {
removeAndInvokeRemoveHook(ch);
invokeDestroyHook(ch);
} else { // Text noderemoveNode(ch.elm); }}}}function removeNode (el) {
var parent = nodeOps.parentNode(el);
// element may have already been removed due to v-html / v-text
if(isDef(parent)) { nodeOps.removeChild(parent, el); }}Copy the code