This paper will analyze four methods related to event distribution on vUE instance, which are:
- $on: listening event
- $off: Removes listening events
- $emit: Triggers the event
- $once: listens to the event once
Firstly, the basic principles of these four methods are introduced in general:
Vue instance creates an object to hold all events to listen for: vm._events = {}
2. Whenever we want to listen for an event, we add a key-value pair to vm._events with the name of the event as the key and an empty array as the value. For example, if the name of the event we want to listen for is event1, then vm._events = {event1: []}
3. Callback functions that listen to events are added to the corresponding array, such as when we call
vm.$on('event1', cb1);
vm.$on('event1', cb2);
vm.$on('event1', cb3);
Copy the code
Vm. _events={event1: [cb1, cb2, cb3]}
4. When we call the remove listener method, all we do is remove the corresponding callback function from the array
vm.$off('event1', cb1);
Copy the code
Vm. _events={event1: [cb2, cb3]}
5. When $emit triggers the corresponding event, all we need to do is to pull out all the callback functions in the array corresponding to the event
vm.$emit('event')
Copy the code
Cb2 and CB3 in event1 are retrieved and executed
$once = $once; $once = $once;
// use the $on method to listen on event2 with the cb4 callback
vm.$on('event2', cb4);
// use the $once method to listen on event2
vm.$once('event2', cb5);
Cb4 and cb5 are executed when event2 is triggered
vm.$emit('event2');
// If event2 is triggered again, cb4 will only be executed, not CB5, which will only be executed once
vm.$emit('event2');
Copy the code
This is the basic rationale for event dispatch methods, but there are also slightly more complex uses of these methods, such as
- $on([‘event1’, ‘event2’], cb
- $off([‘event1’, ‘event2’], cb) // Remove multiple events
- $off() // Remove events without passing parameters
- $off(‘event1’) // Remove event pass a parameter
- $off(‘event1’, cb) // Remove two parameters
- $emit(‘event1’, param1, param2) // Emit event transmission parameters
Read the following source code to know how to deal with these cases
Here is a look at the vue source code in the concrete implementation of the four methods:
$on:
Vue.prototype.$on = function (event, fn) {
const vm = this
// We pass in an array of events to listen for and recursively call the $on method for each event in the array
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
// If there is already a listener event, add the listener callback function to its array, otherwise create a new array and add fn
(vm._events[event] || (vm._events[event] = [])).push(fn)
}
return vm
}
Copy the code
$off:
Vue.prototype.$off = function (event, fn) {
const vm = this
// all
if (!arguments.length) {
// If no arguments are passed, clear the listener function for all events
vm._events = Object.create(null)
return vm
}
// Call $off recursively for each event in the array
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
// Get all the callbacks in the current event
const cbs = vm._events[event]
// If there is no callback function, it returns directly, because there is nothing to remove to listen on
if(! cbs) {return vm
}
// If no callback is specified, all callbacks under this event are removed
if(! fn) { vm._events[event] =null
return vm
}
// Specifies the callback function to remove
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
// Find the callback function to remove from the array corresponding to the event
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break}}return vm
}
Copy the code
$emit:
Vue.prototype.$emit = function (event) {
const vm = this
// Retrieve the list of callback functions corresponding to the trigger event
let cbs = vm._events[event]
if (cbs) {
// the $emit method can pass arguments, which will be passed when the callback function is called
const args = toArray(arguments.1)
// Iterate through the list of callback functions, calling each one
for (let i = 0, l = cbs.length; i < l; i++) {
cbs[i].apply(vm, args)
}
}
return vm
}
}
Copy the code
$once:
Vue.prototype.$once = function (event, fn) {
const vm = this
// encapsulate a higher-order function called on, which calls fn
function on () {
// If the fn event is not executed, the fn event will not be executed
vm.$off(event, on)
// When on is executed, the fn function is executed
fn.apply(vm, arguments)}// This assignment is used in the $off method
$on(event, on); $on(event, on); $on(event, on);
// The function on is added to the event callback array, and fn is removed. We cannot find fn in the callback array, but only on
Fn === fn and remove the on function from the callback array
on.fn = fn
// $once finally calls $on, and the callback is on
vm.$on(event, on)
return vm
}
Copy the code