mini-vue 代码块

<div id="app"></div>
<script>
  // vdom
  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 (let key in vnode.props) {
        let value = vnode.props[key];
        if (key.startsWith('on')) {
          el.addEventListener(key.slice(2).toLocaleLowerCase(), value, false)
        } else {
          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)
  }
  function patch(n1, n2) {
    const el = n2.el = n1.el;
    // props
    const oldProps = n1.props || {}
    const newProps = n2.props || {}

    for (let key in newProps) {
      const oldValue = oldProps[key]
      const newValue = newProps[key]

      if (oldValue !== newValue) {
        el.setAttribute(key, newValue)
      }
    }

    for (let 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') {
        if (newChildren !== oldChildren) {
          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)
          })

        }
      }
    }
  }

  // reactive
  let activeEffect;

  class Dep {
    subscribes = new Set()
    depend() {
      if (activeEffect) {
        this.subscribes.add(activeEffect)
      }
    }
    notify() {
      this.subscribes.forEach(effect => {
        effect()
      })
    }
  }

  function watchEffect(effect) {
    activeEffect = effect
    effect()
    activeEffect = null
  }

  const targetMap = new WeakMap()

  function getDep(target, key) {
    let depMap = targetMap.get(target)
    if (!depMap) {
      depMap = new Map()
      targetMap.set(target, depMap)
    }
    let dep = depMap.get(key)
    if (!dep) {
      dep = new Dep()
      depMap.set(key, dep)
    }
    return dep
  }

  const reactiveHandlers = {
    get(target, key, receiver) {
      const dep = getDep(target, key)
      dep.depend()
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      const dep = getDep(target, key)
      let result = Reflect.set(target, key, value, receiver)
      dep.notify()
      return result
    }
  }

  function reactive(raw) {
    return new Proxy(raw, reactiveHandlers)
  }



  const App = {
    data: reactive({
      count: 1
    }),
    render() {
      return h('div', {
        onClick: () => {
          this.data.count++
        }
      }, String(this.data.count))
    }
  }

  function mountApp(component, container) {
    let isMounted = false;
    let prevVom;

    watchEffect(() => {

      if (!isMounted) {
        prevVom = component.render()
        mount(prevVom, container)
        isMounted = true
      } else {
        const newVdom = component.render()
        patch(prevVom, newVdom)
        prevVom = newVdom
      }
    })
  }
  mountApp(App, document.getElementById('app'))
</script>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值