class Element{
constructor(type, props, children) {
this.type = type
this.props = props
this.children = children
}
}
function createElement (type, props, children) {
return new Element(type, props, children)
}
function render(node) {
let el = document.createElement(node.type)
for(let key in node.props) {
setAttr(el, key, node.props[key])
}
node.children.forEach(child=>{
child = (child instanceof Element) ? render(child) : document.createTextNode(child)
el.appendChild(child)
})
return el
}
function setAttr(node, key, value) {
switch (key) {
case ‘value‘:
if(node.tagName.toLowerCase()===‘input‘||node.tagName.toLowerCase()===‘textarea‘) {
node.value = value
} else {
node.setAttribute(key, value)
}
break;
case ‘style‘:
node.style.cssText = value
break;
default:
node.setAttribute(key, value)
break;
}
}
function renderDom(el, target) {
target.appendChild(el)
}
function diff(oldNode, newNode) {
let patches = {}
walk(oldNode, newNode, 0, patches)
return patches
}
function walk(oldNode, newNode, index, patches) {
let current = []
if(!newNode) {
current.push({type:‘REMOVE‘,index})
} else if(isString(oldNode)&&isString(newNode)) {
if(oldNode!==newNode) {
current.push({type:‘TEXT‘,text:newNode})
}
} else if(oldNode.type === newNode.type) {
let attr = diffAttr(oldNode.props, newNode.props)
if(Object.keys(attr).length>0) current.push({type:‘ATTR‘,attr})
oldNode.children.forEach((child, index)=>{
diffChildren(child, newNode.children[index], patches)
})
} else {
current.push({type:‘REPLAY‘,newNode})
}
if(current.length>0) {
patches[index]=current
}
}
function isString(node) {
return typeof node === ‘string‘
}
function diffAttr(oldNode, newNode, patches) {
let attr = {}
for(let key in oldNode) {
if(oldNode[key] !== newNode[key])
attr[key] = newNode[key]
}
for(let key in newNode) {
if(!oldNode.hasOwnProperty(key))
attr[key] = newNode[key]
}
return attr
}
let num = 0
function diffChildren(oldNode, newNode, patches) {
walk(oldNode, newNode, ++num, patches)
}
let allPatches
let patchIndex = 0
// 这里的node应该是一个真实的Dom对象
function patch(node, patches) {
allPatches = patches
patchWalk(node)
}
function patchWalk(node) {
let current = allPatches[patchIndex++]
let childNodes = node.childNodes
if(current) {
doPatch(node, current)
}
childNodes.forEach(child=>patchWalk(child))
}
function doPatch(node, patches) {
patches.forEach(patch=>{
switch (patch.type) {
case ‘ATTR‘:
for(let key in patch.attr) {
let value = patch.attr[key]
if(value) {
node.setAttribute(key, value)
} else {
node.removeAttribute(key)
}
}
break;
case ‘TEXT‘:
node.textContent = patch.text
break;
case ‘REPLAY‘:
let newNode = pathc.newNode
node.parentNode.replaceChild(newNode,node)
break;
case ‘REMOVE‘:
node.parentNode.removeChild(node)
default:
break;
}
})
}
原文:https://www.cnblogs.com/lbzli/p/12933793.html