Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<meta name="description" content="vue3 deep dive">
<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)
    // props
    if (vnode.props) {
      for (const key in vnode.props) {
        const value = vnode.props[key]
        // property attribute event listener
        el.setAttribute(key, value)
      }
    }
    // 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')
  ])
  function patch(n1, n2) {
    // if (typeof n1 === 'string' && typeof n2 === 'string')
    //   return n2 = n1
    // n2.dom = n1.dom
    // if (n1.tag !== n2.tag) mount(n2, n1.parentNode)
    // for (const key in n1.props) {
    //   if (n1.props[key] !== n2.props[key]) n2.dom.setAttribute(key, n2.props[key])
    // }
    // for (let i = 0; i < n1.children.length; i++) {
    //   patch(n1.children[i], n2.children[i])
    // }
    if (n1.tag === n2.tag) {
      const el = n2.el = n1.el
      // props
      const oldProps = n1.props || {}
      const newProps = n2.props || {}
      // props value changed
      for (const key in newProps) {
        const oldValue = oldProps[key]
        const newValue = newProps[key]
        if (oldValue !== newValue) {
          el.setAttribute(key, newValue)
        }
      }
      // oldProps removed
      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 {
          el.textContent = newChildren
        }
      } else {
        if (typeof oldChildren === 'string') {
          el.innerHTML = ''
          newChildren.forEach(child => {
            mount(child, el)
          })
        } else {
          const commonLength = Math.min(oldChildren.length, newChildren.length)
          for (let i = 0; i < commonLength; i++) {
            patch(oldChildren[i], newChildren[i])
          }
          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)
            })
          }
        }
      }
    }
  }
  mount(vdom, document.querySelector('#app'))
  const vdom2 = h('div', { class: 'green' }, [
    h('span', null, 'changed!')
  ])
  patch(vdom, vdom2)
</script>
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
adjfkspro
0viewers