function diff(oldTree, newTree) {
const patchs = {};
dfs(oldTree, newTree, 0, patchs);
return patchs;
}
function dfs(oldNode, newNode, index, patchs) {
let curPatchs = [];
if (newNode) {
if (oldNode.tagName === newNode.tagName && oldNode.key === newNode.key) {
const props = diffProps(oldNode.props, newNode.props);
curPatchs.push({ type: "changeProps", props });
diffChildren(oldNode.children, newNode.children, index, patchs);
} else {
curPatchs.push({ type: "replaceNode", node: newNode });
}
}
if (curPatchs.length) {
if (patchs[index]) {
patchs[index] = patchs[index].concat(curPatchs);
} else {
patchs[index] = curPatchs;
}
}
}
function diffProps(oldProps, newProps) {
const propsPatchs = [];
for (const key in oldProps) {
if (!newProps.hasOwnProperty(key))
propsPatchs.push({ type: "remove", prop: oldProps[key] });
else if (oldProps[key] !== newProps[key])
propsPatchs.push({
type: "change",
prop: oldProps[key],
value: newProps[k],
});
}
for (const key in newProps) {
if (!oldProps.hasOwnProperty(key))
propsPatchs.push({ type: "add", prop: newProps[key] });
}
return propsPatchs;
}
function diffChildren(oldChild, newChild, index, patchs) {
let { change, list } = diffList(oldChild, newChild, index, patchs);
if (change.length) {
if (patchs[index]) patchs[index] = patchs[index].concat(change);
else patchs[index] = change;
}
oldChild.map((item, i) => {
let keyIndex = list.indexOf(item.key);
if (keyIndex) {
let node = newChild[keyIndex];
dfs(item, node, index, patchs);
}
});
}
function diffList(oldList, newList, index, patchs) {
let change = [];
let list = [];
const newKeys = newList.map((item) => item.key);
oldList.forEach((item) => {
if (newKeys.includes(item.key)) list.push(item.key);
else list.push(null);
});
for (let i = list.length - 1; i >= 0; i--) {
if (!list[i]) {
list.splice(i, 1);
change.push({ type: "remove", index: i });
}
}
newList.forEach((item, i) => {
const { key } = item;
const index = list.indexOf(key);
if (index === -1 || key === null) {
change.push({ type: "add", node: item, index: i });
list.splice(i, 0, key);
} else if (index !== i) {
change.push({ type: "move", form: index, to: i });
move(list, index, i);
}
});
return { change, list };
}