文章首发于我的博客 https://github.com/mcuking/bl...相干代码请查阅 https://github.com/mcuking/bl...
更新属性
本讲接着上一讲 diff 算法简介,次要介绍其中更新属性的机制。
比照新的 vnode 的 props 和老的 vnode 的 props,
- 找到仅存在于新 DOM 节点属性的汇合,调用 setAttrs(olddom, onlyInLeft)
- 找到仅存在于旧 DOM 节点属性的汇合,调用 removeAttrs(olddom, onlyInRight)
- 找到旧 DOM 节点和新 DOM 节点均存在的属性的汇合,调用 diffAttrs(olddom, bothIn.left, bothIn.right)
// 更新属性const {onlyInLeft, onlyInRight, bothIn} = diffObject(vnode.props, olddom._vnode.props)setAttrs(olddom, onlyInLeft)removeAttrs(olddom, onlyInRight)diffAttrs(olddom, bothIn.left, bothIn.right)// 比拟 VNode 与旧 DOM 节点的属性的 交加 差集function diffObject(leftProps, rightProps) { const onlyInLeft = {} // 只存在于新 DOM 节点属性的汇合 const onlyInRight = {} // 只存在于旧 DOM 节点属性的汇合 const bothLeft = {} // 独特存在的属性中新 DOM 节点属性的汇合 const bothRight = {} // 独特存在的属性中旧 DOM 节点属性的汇合 for (let key in leftProps) { if (rightProps[key] === undefined) { onlyInLeft[key] = leftProps[key] } else { bothLeft[key] = leftProps[key] bothRight[key] = rightProps[key] } } for (let key in rightProps) { if (leftProps[key] === undefined) { onlyInRight[key] = rightProps[key] } } return { onlyInLeft, onlyInRight, bothIn: { left: bothLeft, right: bothRight } }}// 设置 DOM 节点属性function setAttrs(dom, props) { for (let k in props) { // 属性为 className 时,改为 class if (k === 'className') { dom.setAttribute('class', props[k]) continue } // 属性为 style 时 if (k === 'style') { if (typeof props[k] === 'string') { dom.style.cssText = props[k] } if (typeof props[k] === 'object') { for (let v in props[k]) { dom.style[v] = props[k][v] } } continue } // 属性为 on 结尾的绑定的事件 if (k[0] === 'o' && k[1] === 'n') { dom.addEventListener(k.substring(2).toLowerCase(), props[k], false) continue } // 其余属性间接赋值 dom.setAttribute(k, props[k]) }}// 去除 DOM 节点属性function removeAttrs(dom, props) { for (let k in props) { if (k === 'className') { dom.removeAttribute('class', props[k]) continue } if (k === 'style') { dom.style.cssText = '' continue } if (k[0] === 'o' && k[1] === 'n') { dom.removeEventListener(k.substring(2).toLowerCase(), props[k], false) continue } // 其余属性间接去除 dom.removeAttribute(k, props[k]) }}// 批改 DOM 节点属性function diffAttrs(dom, newProps, oldProps) { for (let k in newProps) { if (newProps[k] === oldProps[k]) continue if (k === 'className') { dom.setAttribute('class', newProps[k]) continue } if (k === 'style') { if (typeof newProps[k] === 'string') { dom.style.cssText = newProps[k] } if (typeof newProps[k] === 'object' && typeof oldProps[k] === 'object') { for (let v in newProps[k]) { // 若新属性的 css 属性与旧属性的 css 属性不同,则 css 属性赋值为新属性的 css 属性 if (newProps[k][v] !== oldProps[k][v]) { dom.style[v] = newProps[k][v] } } // 若旧属性的 css 属性中某个属性,在新属性的 css 属性中不存在,则将该 css 属性设置为空 for (let v in oldProps[k]) { if (newProps[k][v] === undefined) { dom.style[v] = '' } } } continue } if (k[0] === 'o' && k[1] === 'n') { dom.removeEventListener(k.substring(2).toLowerCase(), oldProps[k], false) dom.addEventListener(k.substring(2).toLowerCase(), newProps[k], false) continue } // 其余属性间接赋值 dom.setAttribute(k, newProps[k]) }}
相干文章
- mini-react 实现原理解说 第一讲
- mini-react 实现原理解说 第二讲
- mini-react 实现原理解说 第三讲
- mini-react 实现原理解说 第四讲
- mini-react 实现原理解说 第五讲