dom更新
本文最后更新于 106 天前,如有失效请评论区留言。

dom更新

接着上面的虚拟dom转换成真实dom后

假设我们的dom以及发生了变化

const vdom = h('div', { class: 'red' }, [
  h('span', null, 'hello world')
])

由之前的vdom变成vdom2,我们需要的就是找出这次变化了什么,已达到最小改动的目的

const vdom2 = h('div', { class: 'green' }, [
  h('span', null, 'changed')
])

需要把之前的mount方法中第一行添加上

function mount (vnode, container) {
  // 把创建出来的真实标签存储一份到vnode的el属性下,方便patch时直接获取
  const el = vnode.el = document.createElement(vnode.tag)
}

写一个patch函数用于更新dom,接受n1,n2参数(老的dom树和新的dom树)

function patch (n1, n2) {
  // 首先要确保他们是同一个标签
  if (n1.tag === n2.tag) {
    const el = n2.el = n1.el
    const oldProps = n1.props || {}
    const newProps = n2.props || {}

    // 如果有了template模板,下面的这些对比都有可能直接跳过(比如检测到静态标签直接跳过对比)

    // 对比新老中是否有key更改了
    for (const key in newProps) {
      const oldValue = oldProps[key]
      const newValue = newProps[key]
      if (newValue != oldValue) {
        el.setAttribute(key, newValue)
      }
    }
    // 当旧的中没有key要去除
    for (const key in oldProps) {
      if (!(key in newProps)) {
        el.removeAttribute(key)
      }
    }
  }
}

接着再去对比children部分

// 接着处理children,可能是一个'string' 或者 []
const oldChildren = n1.children
const newChildren = n2.children
if (typeof newChildren === 'string') {
  if (typeof oldChildren === 'string') {
    // 新老的children都是字符串 不同就直接替换
    if (newChildren !== oldChildren) {
      el.textContent = newChildren
    }
  } else {
    el.textContent = newChildren
  }
} else {
  // 新的children是数组,老的是字符串就遍历循环添加
  if (typeof oldChildren === 'string') {
    el.innerHTML = ''
    newChildren.forEach(child => {
      mount(child, el)
    })
  } else {
    // 当新老的children都是数组,有两种情况,一种使用v-for提供了一个唯一的key,用key去对比
    // 另一种就是双端指针 头头尾尾,vue2中是 头头,尾尾,旧头新尾,旧尾新头
    // 以上四种情况都匹配不到的话,就以新头对旧的vnode进行查找,看是否找到,都找不到就新建元素

    // 这里只是做简单的对比,不一样就直接替换
    const commonLength = Math.min(oldChildren.length, newChildren.length)
    for (let i = 0; i < commonLength; i++) {
      patch(oldChildren[i], newChildren[i])
    }
    // 还需要考虑newChildren更长或者更短的情况下进行新增和删除
    if (newChildren.length > oldChildren.length) {
      newChildren.slice(oldChildren.length).forEach(child => {
        mount(child, el)
      })
    } else if (newChildren.length < oldChildren.length) {
      oldChildren.slice(newChildren.length).forEach(child => {
        el.removeChild(child.el)
      })
    }
  }
}

完整代码如下

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .red {
      color: red;
    }

    .green {
      color: green;
    }
  </style>
</head>

<body>
  <div id="app"></div>
  <script src="https://unpkg.com/vue"></script>
  <script>
    function h (tag, props, children) {
      return {
        tag,
        props,
        children
      }
    }
    function mount (vnode, container) {
      const el = vnode.el = document.createElement(vnode.tag)
      if (vnode.props) {
        for (const key in vnode.props) {
          const value = vnode.props[key]
          el.setAttribute(key, value)
        }
      }
      if (vnode.children) {
        if (typeof vnode.children === 'string') {
          el.textContent = vnode.children
        } else {
          vnode.children.forEach(child => {
            if (typeof child === 'string') {
              el.appendChild(document.createTextNode(child))
            } else {
              mount(child, el)
            }
          })
        }
      }
      container.appendChild(el)
    }
    const vdom = h('div', { class: 'red' }, [
      h('span', null, ['hello', h('div', null, 'ha')])
    ])
    mount(vdom, document.getElementById('app'))


    const vdom2 = h('div', { class: 'green' }, [
      h('span', null, 'changed')
    ])
    function patch (n1, n2) {
      // 首先要确保他们是同一个标签
      if (n1.tag === n2.tag) {
        const el = n2.el = n1.el
        const oldProps = n1.props || {}
        const newProps = n2.props || {}

        // 如果有了template模板,下面的这些对比都有可能直接跳过(比如检测到静态标签直接跳过对比)

        // 对比新老中是否有key更改了
        for (const key in newProps) {
          const oldValue = oldProps[key]
          const newValue = newProps[key]
          if (newValue != oldValue) {
            el.setAttribute(key, newValue)
          }
        }
        // 当旧的中没有key要去除
        for (const key in oldProps) {
          if (!(key in newProps)) {
            el.removeAttribute(key)
          }
        }

        // 接着处理children,可能是一个'string' 或者 []
        const oldChildren = n1.children
        const newChildren = n2.children
        if (typeof newChildren === 'string') {
          if (typeof oldChildren === 'string') {
            // 新老的children都是字符串 不同就直接替换
            if (newChildren !== oldChildren) {
              el.textContent = newChildren
            }
          } else {
            el.textContent = newChildren
          }
        } else {
          // 新的children是数组,老的是字符串就遍历循环添加
          if (typeof oldChildren === 'string') {
            el.innerHTML = ''
            newChildren.forEach(child => {
              mount(child, el)
            })
          } else {
            // 当新老的children都是数组,有两种情况,一种使用v-for提供了一个唯一的key,用key去对比
            // 另一种就是双端指针 头头尾尾,vue2中是 头头,尾尾,旧头新尾,旧尾新头
            // 以上四种情况都匹配不到的话,就以新头对旧的vnode进行查找,看是否找到,都找不到就新建元素

            // 这里只是做简单的对比,不一样就直接替换
            const commonLength = Math.min(oldChildren.length, newChildren.length)
            for (let i = 0; i < commonLength; i++) {
              patch(oldChildren[i], newChildren[i])
            }
            // 还需要考虑newChildren更长或者更短的情况下进行新增和删除
            if (newChildren.length > oldChildren.length) {
              newChildren.slice(oldChildren.length).forEach(child => {
                mount(child, el)
              })
            } else if (newChildren.length < oldChildren.length) {
              oldChildren.slice(newChildren.length).forEach(child => {
                el.removeChild(child.el)
              })
            }
          }
        }
      } else {
        // 当不是同一个类型就直接情况再挂载
        const el = n2.el = n1.el
        el.innerHTML = ''
        mount(n2, el)
      }
    }
    patch(vdom, vdom2)
  </script>
</body>

</html>
版权声明:本文为BIMiracle原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇