mini-react 實現原理講解 第四講

文章首發於個人博客 https://github.com/mcuking/bl...

相關代碼請查閱 https://github.com/mcuking/bl...css

更新屬性

本講接着上一講 diff 算法簡介,主要介紹其中更新屬性的機制。
對比新的 vnode 的 props 和老的 vnode 的 props,node

  • 找到僅存在於新 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])
    }
}

相關文章react

相關文章
相關標籤/搜索