文章首發於個人博客 https://github.com/mcuking/bl...相關代碼請查閱 https://github.com/mcuking/bl...css
本講接着上一講 diff 算法簡介,主要介紹其中更新屬性的機制。
對比新的 vnode 的 props 和老的 vnode 的 props,node
// 更新屬性 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]) } }
相關文章react