export function diff(dom, vnode, container) { const ret = diffNode(dom, vnode) if (container) { container.appendChild(ret) } return ret } function diffNode(dom, vnode) { let out = dom if (vnode === undefined || vnode === null) return if (typeof vnode === 'number') vnode = String(vnode) //文本节点 if (typeof vnode === 'string') { if (dom && dom.nodeType === 3) { if (dom.textContent !== vnode) { //更新文本内容 dom.textContent = vnode } } else { out = document.createTextNode(vnode) if (dom && dom.parentNode) { dom.parentNode.replaceNode(out, dom) } } return out } //非文本DOM节点 if (!dom) { out = document.createElement(vnode.tag) } //比较子节点 if (vnode.childrens && vnode.childrens.length > 0 || (out.childNodes && out.childNodes.length > 0)) { diffChildren(out, vnode.childrens) } diffAttribute(out, vnode) return out } function diffChildren(out, vchildren) { const domChildren = dom.childNodes const children = [] const keyed = {} //将有key的节点(用对象保存)和没有key的节点(用数组保存)分开 if (domChildren.length > 0) { [...domChildren].forEach(item => { const key = item.key if (key) { keyed[key] = item } else { children.push(item) } }) } if (vchildren && vchildren.length > 0) { let min = 0 let childrenLen = children.length [...vchildren].forEach((vchild, i) => { const key = vchild.key let child if (key) { if (keyed[key]) { child = keyed[key] keyed[key] = undefined } else if (childrenLen > min) { for (let j = min; j < childrenLen; j++) { let c = children[j] if (c) { child = c children[j] = undefined if (j === childrenLen - 1) childrenLen--; if (j === min) min++ break; } } } child = diffNode(child, vchild) const f = domChildren[i] if (child && child !== dom && child !== f) { if (!f) { dom.appendChild(child) } else if (child === f.nextSibling) { removeNode(f) } else { dom.insertBefore(child, f) } } } }) } } function diffAttribute(dom, vnode) { //dom 是原有的节点对象 vnode虚拟dom const oldAttrs = {} const domAttrs = dom.attributes [...domAttrs].forEach(item => { oldAttrs[item.name] = item.value }) for (let key in newAttrs) { if (!(key in oldAttrs)) { setAttribute(dom, key, undefined) // 设置属性 } } //更新 for (let key in newAttrs) { if (oldAttrs[key] !== newAttrs[key]) { setAttribute(dom, key, newAttrs[key]) } } }