04-Vue的事件机制

事件机制无非就是一个添加监听和触发的过程。所以可想而知应该有on方法和trigger方法,这是jQuery的名字。Vue也有类似的方法,在core/instance/events.js中。

有以下四个方法,在Vue.prototype上:

  • $on
  • $once
  • $off
  • $emit

总体上这段代码没有太多可说的。要知道一点,Vue实例上的事件监听函数都是保存在这个实例的_events上的。每个类型的事件监听函数对应一个字段,它是一个数组。

const hookRE = /^hook:/
  // 注册事件监听函数
  Vue.prototype.$on = function (event: string | Array, fn: Function): Component {
    const vm: Component = this
    if (Array.isArray(event)) {
      // 如果是数组,表示多个事件共用一个监听函数,所以循环调用
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$on(event[i], fn)
      }
    } else {
      // Vue实例的事件监听函数放在了_events对象中,每个类型对应一个属性,属性值是一个数组
      (vm._events[event] || (vm._events[event] = [])).push(fn)
      // optimize hook:event cost by using a boolean flag marked at registration
      // instead of a hash lookup
      if (hookRE.test(event)) {
        vm._hasHookEvent = true
      }
    }
    return vm
  }

  Vue.prototype.$once = function (event: string, fn: Function): Component {
    const vm: Component = this
    function on () {
      // 保证只调用一次
      vm.$off(event, on)
      fn.apply(vm, arguments)
    }
    on.fn = fn
    vm.$on(event, on)
    return vm
  }

  // 解绑事件监听函数
  Vue.prototype.$off = function (event?: string | Array, fn?: Function): Component {
    const vm: Component = this
    // all
    // 没参数,就清空所有的事件监听函数
    if (!arguments.length) {
      vm._events = Object.create(null)
      return vm
    }
    // array of events
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$off(event[i], fn)
      }
      return vm
    }
    // specific event
    const cbs = vm._events[event]
    if (!cbs) {
      return vm
    }
    if (!fn) {
      vm._events[event] = null
      return vm
    }
    // specific handler
    let cb
    let i = cbs.length
    while (i--) {
      cb = cbs[i]
      if (cb === fn || cb.fn === fn) {
        cbs.splice(i, 1)
        break
      }
    }
    return vm
  }

  Vue.prototype.$emit = function (event: string): Component {
    const vm: Component = this
    if (process.env.NODE_ENV !== 'production') {
      const lowerCaseEvent = event.toLowerCase()
      if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
        tip(
          `Event "${lowerCaseEvent}" is emitted in component ` +
          `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
          `Note that HTML attributes are case-insensitive and you cannot use ` +
          `v-on to listen to camelCase events when using in-DOM templates. ` +
          `You should probably use "${hyphenate(event)}" instead of "${event}".`
        )
      }
    }
    // 拿到指定类型的事件处理函数数组
    let cbs = vm._events[event]
    if (cbs) {
      cbs = cbs.length > 1 ? toArray(cbs) : cbs
      const args = toArray(arguments, 1)
      const info = `event handler for "${event}"`
      for (let i = 0, l = cbs.length; i < l; i++) {
        invokeWithErrorHandling(cbs[i], vm, args, vm, info)
      }
    }
    return vm
  }

你可能感兴趣的:(Vue源码)