[Vue] MIni mount and patch function
<style>
.red {color: red;}
.green {color: green;}
</style>
<div id="app"></div>
<script>
function h(tag, props, children) {
return { tag, props, children };
}
function mount(vnode, container) {
const el = vnode.el = document.createElement(vnode.tag)
console.log(vnode)
// props
if (vnode.props) {
for (const key in vnode.props) {
el.setAttribute(key, vnode.props[key])
}
}
// children
if (vnode.children) {
if (typeof vnode.children === 'string') {
el.textContent = vnode.children
} else {
vnode.children.forEach(child => {
mount(child, el)
})
}
}
container.appendChild(el)
}
const vdom = h('div', {class: 'red'}, [
h('span', null, 'hello')
])
mount(vdom, document.getElementById('app'))
function patch(/*old vdom*/n1 , /*new vdom*/ n2 ) {
if (
n1.tag === n2.tag
) {
const el = n2.el = n1.el
// props
const oldProps = n1.props || {}
const newProps = n2.props || {}
for (const key in newProps) {
const oldValue = oldProps[key]
const newValue = newProps[key]
if (oldValue !== newValue) {
el.setAttribute(key, newValue)
}
}
for(const key in oldProps) {
if (!(key in newProps)) {
el.removeAttribute(key)
}
}
// children
const oldChildren = n1.children
const newChildren = n2.children
if (typeof newChildren === 'string') {
if (typeof oldChildren === 'string') {
el.textContent = newChildren
} else {
// oldChildren is array
el.textContent = newChildren
}
} else {
// newChildren is array
if (typeof oldChildren === 'string') {
el.innerHTML = ''
newChildren.forEach(child => {
mount(child, el)
})
} else {
// oldChildren is array
// handle common children
const commonLength = Math.min(oldChildren.length, newChildren.length)
for (let i = 0; i < commonLength; i++) {
patch(oldChildren[i], newChildren[i])
}
// newChildren is longer, handle new children
if (newChildren.length > oldChildren.length) {
newChildren.slice(oldChildren.length).forEach(child => {
mount(child, el)
})
} else if (newChildren.length < oldChildren.length) {
// oldChildren is longer, handle removed children
oldChildren.slice(newChildren.length).forEach(child => {
el.removeChild(child.el)
})
}
}
}
} else {
// replace
}
}
const vdom2 = h('div', {class: 'green'}, [
h('span', null, 'changed!')
])
patch(vdom, vdom2)
</script>