VueStudyNote icon indicating copy to clipboard operation
VueStudyNote copied to clipboard

18 实现emit

Open xwjie opened this issue 6 years ago • 0 comments

实现起来很简单,因为之前的代码已经处理了x-on(@),所以直接绑定即可。

实现

框架代码增加 _events字段和$emit方法

class Xiao{

  // 事件
  _events : any

  /**
   * 调用事件
   *
   * @param {*} event
   */
  $emit(event: String){
    // 无需绑定this,方法生成的时候已经绑定了
    this._events[event]()
  }
}

更新组件的时候的时候,如果是子组件,初始化数据。


function updateComponent(vm: Xiao) {
  let proxy = vm

  // 虚拟dom里面的创建函数
  proxy.h = h

  // 新的虚拟节点
  // 指令的信息已经自动附带再vnode里面
  let vnode = vm.$render.call(proxy)

  // 把实例绑定到vnode中,处理指令需要用到
  setContext(vnode, vm)

  // 处理子组件
  setComponentHook(vnode, vm)

  // 其他代码
 ******
}

/**
 * 实现组件功能
 *
 * 采用snabbdom的hook,在insert和update的时候更新数据。
 *
 * @param {*} vnode
 * @param {*} vm
 */
function setComponentHook(vnode: any, vm: Xiao) {
  if (!vnode.sel) {
    return
  }

  // 查看是否组成了组件?
  const Comp = Xiao.component(vnode.sel)

  if (Comp) {
    vnode.data.hook = {
      insert: (vnode) => {
        log('component vnode', vnode)

        // 创建子组件实例
        let app = new Comp()
        app.$parent = vm

        const propsData = vnode.data.props

        // 把计算后的props数据代理到当前vue里面
        initProps(app, propsData)

        // 绑定事件
        if(vnode.data.on){
          initEvent(app, vnode.data.on)
        }

        // 保存到vnode中,更新的时候需要取出来用
        vnode.childContext = app

        // 渲染
        app.$mount(vnode.elm)
      },
      update: (oldvnode, vnode) => {
        const app = oldvnode.childContext

        // 更新update属性
        updateProps(app, vnode.data.props)

        vnode.childContext = app
      }

    }
  }

  // 递归
  if (vnode.children) {
    vnode.children.forEach(function (e) {
      setComponentHook(e, vm)
    }, this)
  }

}



/**
 * 绑定事件
 *
 * @param {*} vm
 * @param {*} on
 */
export function initEvent(vm: any, on: Object) {
  log('initEvent', on)
  vm._events = on
}

测试代码

<h1>$emit测试</h1>
<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter @increment="incrementTotal"></button-counter>
  <button-counter @increment="incrementTotal2"></button-counter>
</div>
<script>

Xiao.component('button-counter', {
  template: '<button @click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      console.log('incrementCounter')
      this.counter += 1
      this.$emit('increment')
    }
  },
})

new Xiao({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    },
    incrementTotal2: function () {
      this.total -= 1
    }
  }
})
</script>

xwjie avatar Jan 21 '18 15:01 xwjie