Source: 2.diff.js
const nodeOps = {
setTextContent(text) {
if (platform === "weex") {
node.parentNode.setAttr("value", text);
} else if (platform === "web") {
node.textContent = text;
}
},
parentNode() {
//......
},
removeChild() {
//......
},
nextSibling() {
//......
},
insertBefore() {
//......
},
};
// insert ç¨æ¥å¨ parent è¿ä¸ªç¶èç¹ä¸æå
¥ä¸ä¸ªåèç¹ï¼å¦ææå®äº ref åæå
¥å° ref è¿ä¸ªåèç¹åé¢
function insert(parent, elm, ref) {
if (parent) {
if (ref) {
if (ref.parentNode === parent) {
nodeOps.insertBefore(parent, elm, ref);
}
} else {
nodeOps.appendChild(parent, elm);
}
}
}
// createElm ç¨æ¥æ°å»ºä¸ä¸ªèç¹ï¼ tag åå¨å建ä¸ä¸ªæ ç¾èç¹ï¼å¦åå建ä¸ä¸ªææ¬èç¹ã
function createElm(vnode, parentElm, refElm) {
if (vnode.tag) {
insert(parentElm, nodeOps.createElement(vnode.tag), refElm);
} else {
insert(parentElm, nodeOps.createTextNode(vnode.text), refElm);
}
}
// addVnodes ç¨æ¥æ¹éè°ç¨ createElm æ°å»ºèç¹ã
function addVnodes(parentElm, refElm, vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
createElm(vnodes[startIdx], parentElm, refElm);
}
}
// removeNode ç¨æ¥ç§»é¤ä¸ä¸ªèç¹ã
function removeNode(el) {
const parent = nodeOps.parentNode(el);
if (parent) {
nodeOps.removeChild(parent, el);
}
}
// removeVnodes 伿¹éè°ç¨ removeNode ç§»é¤èç¹ã
function removeVnodes(parentElm, vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
const ch = vnodes[startIdx];
if (ch) {
removeNode(ch.elm);
}
}
}
// diff ç®æ³æ¯éè¿åå±çæ èç¹è¿è¡æ¯è¾èé对æ è¿è¡éå±æç´¢éåçæ¹å¼
// æä»¥æ¶é´å¤æåº¦åªæ O(n)ï¼æ¯ä¸ç§ç¸å½é«æçç®æ³
function patch(oldVnode, vnode, parentElm) {
if (!oldVnode) {
// 没æèèç¹ï¼æå
¥æ°èç¹
addVnodes(parentElm, null, vnode, 0, vnode.length - 1);
} else if (!vnode) {
// æ²¡ææ°èç¹ï¼å é¤èèç¹
removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1);
} else {
// 妿æ¯åä¸ä¸ªèç¹ï¼patchVnode
if (sameVnode(oldVNode, vnode)) {
patchVnode(oldVNode, vnode);
} else {
// 䏿¯ä¸ä¸ªèç¹ï¼å é¤èèç¹ï¼æ°å¢æ°èç¹
removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1);
addVnodes(parentElm, null, vnode, 0, vnode.length - 1);
}
}
}
// ä»ä¹æ
åµä¸ä¸¤ä¸ª VNode ä¼å±äº sameVnode ï¼ç¸åçèç¹ï¼å¢ï¼
function sameVnode() {
return (
a.key === b.key &&
a.tag === b.tag &&
a.isComment === b.isComment &&
!!a.data === !!b.data &&
sameInputType(a, b)
);
}
function sameInputType(a, b) {
if (a.tag !== "input") return true;
let i;
const typeA = (i = a.data) && (i = i.attrs) && i.type;
const typeB = (i = b.data) && (i = i.attrs) && i.type;
return typeA === typeB;
}
/**
* æ ¸å¿æ¹æ³
* @param {*} oldVnode
* @param {*} vnode
* @returns
*/
function patchVnode(oldVnode, vnode) {
// æ°è VNode èç¹ç¸åçæ
åµä¸ï¼å°±ä¸éè¦å任使¹åäºï¼ç´æ¥ return æ
if (oldVnode === vnode) {
return;
}
// å¨å½æ°è VNode èç¹é½æ¯ isStaticï¼éæçï¼ï¼å¹¶ä¸ key ç¸åæ¶ï¼
// åªè¦å° componentInstance ä¸ elm ä»è VNode èç¹âæ¿è¿æ¥âå³å¯ã
// è¿éç isStatic ä¹å°±æ¯å颿å°è¿çãç¼è¯ãçæ¶åä¼å°éæèç¹æ è®°åºæ¥ï¼è¿æ ·å°±å¯ä»¥è·³è¿æ¯å¯¹çè¿ç¨ã
if (vnode.isStatic && oldVnode.isStatic && vnode.key === oldVnode.key) {
vnode.elm = oldVnode.elm;
vnode.componentInstance = oldVnode.componentInstance;
return;
}
const elm = (vnode.elm = oldVnode.elm);
const oldCh = oldVnode.children;
const ch = vnode.children;
// 彿° VNode èç¹æ¯ææ¬èç¹çæ¶åï¼ç´æ¥ç¨ setTextContent æ¥è®¾ç½® text
if (vnode.text) {
nodeOps.setTextContent(elm, vnode.text);
} else {
// oldCh ä¸ ch é½åå¨ä¸ä¸ç¸åæ¶ï¼ä½¿ç¨ updateChildren 彿°æ¥æ´æ°åèç¹
if (oldCh && ch && oldCh !== ch) {
updateChildren(elm, oldCh, ch);
} else if (ch) {
// å¦æåªæ ch åå¨çæ¶å
// 妿èèç¹æ¯ææ¬èç¹åå
å°èç¹çææ¬æ¸
é¤
if (oldVnode.text) nodeOps.setTextContent(elm, "");
// ç¶åå° ch æ¹éæå
¥å°èç¹elmä¸ã
addVnodes(elm, null, ch, 0, ch.length - 1);
} else if (oldCh) {
// åçå½åªæ oldch å卿¶ï¼è¯´æéè¦å°èèç¹éè¿ removeVnodes å
¨é¨æ¸
é¤
removeVnodes(elm, oldCh, 0, oldCh.length - 1);
} else if (oldVnode.text) {
// æåä¸ç§æ
嵿¯å½åªæèèç¹æ¯ææ¬èç¹çæ¶åï¼æ¸
é¤å
¶èç¹ææ¬å
容ã
nodeOps.setTextContent(elm, "");
}
}
}
function updateChildren(parentElm, oldCh, newCh) {
let oldStartIdx = 0;
let newStartIdx = 0;
let oldEndIdx = oldCh.length - 1;
let oldStartVnode = oldCh[0];
let oldEndVnode = oldCh[oldEndIdx];
let newEndIdx = newCh.length - 1;
let newStartVnode = newCh[0];
let newEndVnode = newCh[newEndIdx];
let oldKeyToIdx, idxInOld, elmToMove, refElm;
// while 循ç¯ï¼å¨è¿è¿ç¨ä¸ï¼oldStartIdxãnewStartIdxãoldEndIdx 以å newEndIdx ä¼éæ¸åä¸é´é æ¢
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (!oldStartVnode) {
oldStartVnode = oldCh[++oldStartIdx];
} else if (!oldEndVnode) {
oldEndVnode = oldCh[--oldEndIdx];
} else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
} else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldStartVnode, newEndVnode)) {
patchVnode(oldStartVnode, newEndVnode);
nodeOps.insertBefore(
parentElm,
oldStartVnode.elm,
nodeOps.nextSibling(oldEndVnode.elm)
);
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldEndVnode, newStartVnode)) {
patchVnode(oldEndVnode, newStartVnode);
nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
} else {
let elmToMove = oldCh[idxInOld];
if (!oldKeyToIdx)
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
idxInOld = newStartVnode.key ? oldKeyToIdx[newStartVnode.key] : null;
// å½ånewStartVnodeå¨oldè¿æªéåå®ç项ç®ä¸ï¼æ¯å¦åå¨
if (!idxInOld) {
createElm(newStartVnode, parentElm);
newStartVnode = newCh[++newStartIdx];
} else {
elmToMove = oldCh[idxInOld];
if (sameVnode(elmToMove, newStartVnode)) {
patchVnode(elmToMove, newStartVnode);
oldCh[idxInOld] = undefined;
nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm);
newStartVnode = newCh[++newStartIdx];
} else {
createElm(newStartVnode, parentElm);
newStartVnode = newCh[++newStartIdx];
}
}
}
}
// å½ while 循ç¯ç»æä»¥åï¼å¦æ oldStartIdx > oldEndIdxï¼è¯´æèèç¹æ¯å¯¹å®äº
// 使¯æ°èç¹è¿æå¤çï¼éè¦å°æ°èç¹æå
¥å°çå® DOM ä¸å»ï¼è°ç¨ addVnodes å°è¿äºèç¹æå
¥å³å¯
if (oldStartIdx > oldEndIdx) {
refElm = newCh[newEndIdx + 1] ? newCh[newEndIdx + 1].elm : null;
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx);
} else if (newStartIdx > newEndIdx) {
// å¦ææ»¡è¶³ newStartIdx > newEndIdx æ¡ä»¶ï¼è¯´ææ°èç¹æ¯å¯¹å®äºï¼èèç¹è¿æå¤
// å°è¿äºæ ç¨çèèç¹éè¿ removeVnodes æ¹éå é¤å³å¯ã
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
}
}
function createKeyToOldIdx(children, beginIdx, endIdx) {
let i, key;
const map = {};
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key;
if (isDef(key)) map[key] = i;
}
return map;
}