function diff(oldVnode, newVnode) {
if (!oldVnode) {
return createVnode(newVnode); // 新增节点
}
if (!newVnode) {
return destroyVnode(oldVnode); // 删除节点
}
if (isSameVnode(oldVnode, newVnode)) {
patchVnode(oldVnode, newVnode); // 更新节点
} else {
replaceVnode(oldVnode, newVnode); // 替换节点
}
}
function isSameVnode(oldVnode, newVnode) {
return oldVnode.key === newVnode.key && oldVnode.tag === newVnode.tag;
}
function createVnode(vnode) {
// 创建真实DOM并插入到文档中
const el = document.createElement(vnode.tag);
if (vnode.text) {
el.textContent = vnode.text;
} else if (vnode.children) {
vnode.children.forEach(child => {
el.appendChild(createVnode(child));
});
}
vnode.el = el; // 将真实DOM挂载到vnode上
return el;
}
function destroyVnode(vnode) {
if (vnode.el) {
vnode.el.parentNode.removeChild(vnode.el);
}
}
function patchVnode(oldVnode, newVnode) {
newVnode.el = oldVnode.el; // 复用DOM元素
// 更新文本节点
if (newVnode.text !== undefined && newVnode.text !== oldVnode.text) {
oldVnode.el.textContent = newVnode.text;
}
// 更新子节点
if (oldVnode.children && newVnode.children) {
updateChildren(oldVnode.el, oldVnode.children, newVnode.children);
} else if (newVnode.children) { // 老节点没有子节点,新节点有子节点
newVnode.children.forEach(child => {
oldVnode.el.appendChild(createVnode(child));
});
} else if (oldVnode.children) { // 老节点有子节点,新节点没有子节点
oldVnode.el.innerHTML = '';
}
}
function replaceVnode(oldVnode, newVnode) {
const newEl = createVnode(newVnode);
oldVnode.el.parentNode.replaceChild(newEl, oldVnode.el);
destroyVnode(oldVnode);
}
function updateChildren(parentElm, oldCh, newCh) {
// 简化版,只实现了简单的头尾比较,没有实现双指针算法
let oldStartIdx = 0;
let newStartIdx = 0;
let oldEndIdx = oldCh.length - 1;
let newEndIdx = newCh.length - 1;
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isSameVnode(oldCh[oldStartIdx], newCh[newStartIdx])) {
patchVnode(oldCh[oldStartIdx], newCh[newStartIdx]);
oldStartIdx++;
newStartIdx++;
} else if (isSameVnode(oldCh[oldEndIdx], newCh[newEndIdx])) {
patchVnode(oldCh[oldEndIdx], newCh[newEndIdx]);
oldEndIdx--;
newEndIdx--;
} else {
// 其他情况,为了简化,直接替换
replaceVnode(oldCh[oldStartIdx], newCh[newStartIdx]);
oldStartIdx++;
newStartIdx++;
}
}
// 添加新节点
if (newStartIdx <= newEndIdx) {
for (let i = newStartIdx; i <= newEndIdx; i++) {
parentElm.appendChild(createVnode(newCh[i]));
}
}
// 删除老节点
if (oldStartIdx <= oldEndIdx) {
for (let i = oldStartIdx; i <= oldEndIdx; i++) {
destroyVnode(oldCh[i]);
}
}
}
// Example usage (simplified vnode representation):
const oldVnode =