This function mainly mounts custom events “on”, “on”, “on”, “once”, “off”, “off”, “off”, “emit” on vue’s “Prototype” object.

Let’s look at the code of these functions in detail:

$on

 Vue.prototype.$on = function (event: string | Array<string>, 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 {
      (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
  }
Copy the code

As you can see, $on takes two parameters, event and fn, which are the event name and event handler respectively. The event name can be an array, which means that different events are bound to the same event handler.

(vm._events[event] || (vm._events[event] = [])).push(fn)
Copy the code

If you look at the above line of code, you can see that defining duplicate event names is separate and will not be combined into one event.

// const hookRE = /^hook:/
if (hookRE.test(event)) {
  vm._hasHookEvent = true
}
Copy the code

This line of code sets the _hasHookEvent variable to true when your event name starts with hook:. I’m going to call this a hook event, which means that when a lifecycle hook is called, it triggers the corresponding hook event, For example, (on(‘hook:created’, fn), on(‘hook:created’, FN), on(‘hook:created’, FN), on(‘ Hook :mounted’, FN)).

$once

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
  }
Copy the code

$on = $emit; $on = $emit; $on = $emit; $on = $emit; $on = $emit; And call apply to change this, which is equivalent to whether fn is an arrow function or not.

$off

Vue.prototype.$off = function (event? : string | Array<string>, 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 }Copy the code

As you can see, $off also takes two parameters, but both are optional. First check to see if there are any arguments, and if they are useful, set _events to null, which clears all events. Then check whether the event is an array and recursively call $off.

if (! cbs) { return vm }Copy the code

If the event name does not exist, return VM is no longer executed.

if (! fn) { vm._events[event] = null return vm }Copy the code

If the FN event does not exist, all listeners for that event are removed.

let cb
    let i = cbs.length
    while (i--) {
      cb = cbs[i]
      if (cb === fn || cb.fn === fn) {
        cbs.splice(i, 1)
        break
      }
  }
Copy the code

If the fn argument is equal to cb, or to cb.fn, it is removed from the array.

$cb.fn = on. Fn = fn

$emit

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 // processes the second argument to the $emit function, Arguments [1] 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 }Copy the code

If the event name is lowerCaseEvent and the event handler does not exist, tip will be displayed.

let cbs = vm._events[event]
Copy the code

Gets all event event handlers. Finally, invokeWithErrorHandling is called to trigger the $ON or $once binding event handler. Finally, return to the VM.

Reprinted from the author: lovers vue link: www.jianshu.com/p/f34e78bfe… The copyright of the book belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please indicate the source.