00 preface
$dispatch and $broadcast, as a pair of 💑 attributes, were primarily used in Vue 1.0 to implement event flow messages based on a component tree structure — communication between nested parent and child components by bubbling event flows up and down. However, it was removed in Vue 2.0 due to its obvious functional defects. Although the Vue website no longer supports component communication using $Dispatch and $broadcast, it is packaged in many Vue based UI frameworks, including Element-UI, iView, and so on.
So how do $dispatch and $broadcast work, and what are the underlying implementations? Next, let’s talk about it in detail!
01 $dispatch
Break down
To get to the bottom of it, let’s go to the Vue 1.0 documentation and see the concept.
Concept:
Dispatch an event, first triggering it on the instance itself, and then propagates upward along the parent chain. The propagation stops when it triggers a parent event listener, unless that listener returns true
Any additional arguments will be passed into the listener’s callback function.
Dispatch is an event that is triggered on its own instance and propagated up the parent chain. Propagation stops when it fires an event listener on the parent component, unless the listener returns true. Any other arguments are passed to the listener’s callback function.
Parameters:
Dispatch receives two arguments: Event is the name of the event and […args] is the argument passed to the callback function when the event is triggered.
* * example:
// Create a parent component
var parent = new Vue();
// Create a child1 component whose parent points to parent
var child1 = new Vue({ parent: parent });
// Create a child2 component whose parent points to child1
var child2 = new Vue({ parent: child1 });
// The parent component listens for an event named test and binds a callback function
parent.$on('test'.function () {
console.log('parent notified');
});
// The child1 component listens for an event named test and binds a callback function
child1.$on('test'.function () {
console.log('child1 notified');
});
// The child2 component listens for an event named test and binds a callback function
child2.$on('test'.function () {
console.log('child2 notified');
});
Copy the code
The relationship between the parent, child1, and child2 components can be shown as follows:
// Trigger the test event with Dispatch in the Child2 component
child2.$dispatch('test');
// Event execution outputs the following results
// -> "child2 notified"
// -> "child1 notified"
Copy the code
When performing child2 $dispatch (” test “); “, the callback function for the test event that is listened on in the CHILD2 component is triggered and “child2 notified” is printed. The event is transmitted up the component chain and to the CHILD1 component, The listener event outputs “child1 notified”, but the listener does not return true, so the event is notified and the output is only “child2 notified” and “child1 notified”.
Vue 1.0 official implementation
In Vue 1.0 version, $dispatch implementation source code in/SRC/instance/API/events. The js file, the code is simple:
/** * Recursively propagate an event up the parent chain. * @param {String} event * @param {... *} additional arguments */
The $dispatch method is defined on Vue's prototype
// Accepts the event name as a string
Vue.prototype.$dispatch = function (event) {
// First execute the $emit trigger event to store the return value in shouldPropagate
var shouldPropagate = this.$emit.apply(this.arguments)
If the first execution of the $emit method returns a value other than true, it returns
// If the return value is not true, the component logic does not want the event to continue to the parent component
if(! shouldPropagate)return
// If the $emit method returns true for the first time, get the parent component instance of the current component
var parent = this.$parent
// Convert the parameters accepted by the function to an array
var args = toArray(arguments)
// use object event to indicate non-source emit on parents
// Assemble an object from the parameters of the event name passed in
args[0] = { name: event, source: this }
// The loop knows the parent of the component
while (parent) {
// Execute the $emit trigger event in the parent component
shouldPropagate = parent.$emit.apply(parent, args)
// If the parent $emit returns true then continue recursively to the parent, otherwise the loop is stopped
parent = shouldPropagate ? parent.$parent : null
}
// Finally returns the current component instance
return this
}
Copy the code
Element – the UI
In element-UI, the source code for the $dispatch implementation is placed in the/SRC /mixins/ Emitter.js file. The code is simple:
// Define a dispatch method that takes three parameters: the component name, the name of the event to fire, and the parameters passed by the callback function
dispatch(componentName, eventName, params) {
// Get the parent component instance based on the current component, which is compatible with the root component instance
var parent = this.$parent || this.$root;
// Get the component name from the parent component's $option property
var name = parent.$options.componentName;
// Executes a loop when a parent instance of the current component exists, and when the parent name does not exist or is not equal to the component name passed in
while(parent && (! name || name ! == componentName)) {// Record the parent of the parent component
parent = parent.$parent;
// Gets the name of the parent component when its parent exists
if(parent) { name = parent.$options.componentName; }}// When the loop ends, the value of parent is the final matched component instance
if (parent) {
// Call the $emit method when parent exists
// Pass in an array of parent instances, event names, and params parameters
// Triggers an event with the same name as eventNameparent.$emit.apply(parent, [eventName].concat(params)); }}Copy the code
Variance analysis
After a careful look at the two versions of the $Dispatch code, do you find that the two versions of the implementation and functionality is very different.
-
1. Accept parameters: The Vue implementation will only accept a string event name as a parameter, whereas the Elemental-UI implementation will accept three parameters: the name of the component to fire the event, the name of the event to fire, and the parameter passed by the callback function.
-
2. Functions: The Vue implementation will trigger events up the component chain until the listener in the parent component does not return true, during which time all components will execute the event response, including the current component itself, and the Elemental-UI implementation will continuously iterate over the parent component based on the current component. Until a match is found with the accepted component name, the traversal stops and listening events in the matching component are emitted.
10 $broadcast
Break down
With the $Dispatch implementation and Vue implementation being different from the Elemental-UI implementation, it’s time to talk about $broadcast, because they are couples.
concept
Broadcast an event that propagates downward to all descendants of the current instance. Since the descendants expand into multiple sub-trees, The propagation for each path will stop when a listener callback is fired along that path, unless the callback returnstrue
.
broadcast
Is an event that propagates down to all descendants of the current instance. Since descendants extend to multiple subtrees, event propagation will follow many different “paths.” Unless the callback returns true, propagation will stop for each path when the listener callback is triggered along that path.
parameter
Broadcast receives two arguments: event is the name of the event, and […args] is the argument passed to the callback function when the event is triggered.
example
// Create the parent component instance
var parent = new Vue()
// Create a component instance of child1 with its parent pointing to parent
var child1 = new Vue({ parent: parent })
// Create a child2 component instance with its parent pointing to parent
var child2 = new Vue({ parent: parent })
// Create a component instance of child3 with its parent pointing to child2
var child3 = new Vue({ parent: child2 })
// The child1 component listens for an event named test and binds a callback function
child1.$on('test'.function () {
console.log('child1 notified')})// The child2 component listens for an event named test and binds a callback function
child2.$on('test'.function () {
console.log('child2 notified')})// The child3 component listens for an event named test and binds a callback function
child3.$on('test'.function () {
console.log('child3 notified')})Copy the code
The relationship between the four components parent, child1, child2, and child3 can be shown in the following diagram:
parent.$broadcast('test')
// -> "child1 notified"
// -> "child2 notified"
Copy the code
When performing the parent $broadcast (” test “); According to the order of event binding, the event flow will trigger the binding event in child1 first, although the parent component has two siblings child1 and child2. “Child1 notified” is output, and the event stream reaches the Child2 component, which triggers a binding event in the Child2 component and outputs” Child2 notified”. At this point, the listener in the Child2 component does not return true, so event transmission ends there and the final output is just “child1 notified” and “child2 notified”.
Vue 1.0 official implementation
In Vue 1.0 version, $broadcast implementation source code in/SRC/instance/API/events. The js file, the code is simple:
/** * Recursively broadcast an event to all child instances. * @param {String|Object} event * @param {... *} additional arguments */
The $dispatch method is defined on Vue's prototype
// Accept an event
Vue.prototype.$broadcast = function (event) {
// Get the type of the incoming event and determine if it is a string
var isSource = typeof event === 'string'
If the event type is a string, use the event name attribute if it is not a string
event = isSource ? event : event.name
// if no child has registered for this event,
// then there's no need to broadcast.
// If a child of the current component does not register the event, return it directly, without broadcast
if (!this._eventsCount[event]) return
// Get the children of the current component
var children = this.$children
// Convert the parameters accepted by the function to an array
var args = toArray(arguments)
// If the incoming event is a string
if (isSource) {
// use object event to indicate non-source emit
// on children
// Assemble an object from the parameters of the event name passed in
args[0] = { name: event, source: this}}// Loop child components
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i]
// Call the $emit trigger event in each child component
var shouldPropagate = child.$emit.apply(child, args)
// Determine if the value returned by calling $emit is true
if (shouldPropagate) {
If a call to $emit returns true, the recursive grandson component continues broadcasting
child.$broadcast.apply(child, args)
}
}
// Finally returns an instance of the current component
return this
}
Copy the code
Element – the UI
In element-UI, the source code for the $broadcast implementation is placed in/SRC /mixins/ Emitter.js. The code is very simple:
// Define the broadcast method that takes three parameters: the component name, the name of the event to be fired, and the parameters passed by the callback function
function broadcast(componentName, eventName, params) {
// Loop through the children of the current component
this.$children.forEach(child= > {
// Get the name of each child component
var name = child.$options.componentName;
// Determine if the name of the child component is equal to the name of the component passed in
if (name === componentName) {
If the name of the child component is equal to the name of the component passed in, the $emit event is called
child.$emit.apply(child, [eventName].concat(params));
} else {
// The broadcast grandson component is called recursively if the name of the child component is not equal to the name of the component passed inbroadcast.apply(child, [componentName, eventName].concat([params])); }}); }Copy the code
Variance analysis
As with $dispatch, there are big differences between the two implementations of $broadcast:
-
1. Accept parameters: The Vue implementation will only accept a string event name as a parameter, whereas the Elemental-UI implementation will accept three parameters: the name of the component to fire the event, the name of the event to fire, and the parameter passed by the callback function.
-
2. Implementation functions: The default $broadcast trigger mode implemented by Vue is to trigger only the descendant component, not the descendant component. If the descendant creates a listener and returns true, the event will be transmitted to the descendant component. The version of the Element-UI implementation passes directly to all descendant components, and does not trigger a listening event for the current child until the name of the child component is equal to the name of the component passed in, with no return value.
11 summary
That concludes the $dispatch and $broadcast tutorial. You probably already know why Vue 2.0 removed these two properties. First, let’s introduce the statement on the official website:
This is because the event-flow approach based on the component tree structure is difficult to understand and becomes more and more fragile as the component structure expands. This is not a good way of doing things, and we don’t want to cause developers too much pain in the future. and$dispatch
和 $broadcast
Communication between sibling components is also not addressed.
So $dispatch and $broadcast do have this problem. When certain conditions are met, the event stream of the current component will be triggered. The function of $broadcast is to stream the event stream from the current component to the child component. A listening event for the current child component is emitted when certain conditions are met. That is to say, $dispatch and $broadcast mainly solve the communication problem of parent-child components and nested parent-child components, but do not solve the communication problem of sibling components. On the other hand, such event flow mode is based on the component tree structure, which can become extremely tedious when the business becomes more and more complicated. It can even be messy and difficult to maintain, so it is not surprising that Vue 2.0 removes both apis.
But why does a tripartite UI library encapsulate a similar way for components to communicate? My guess is that it is possible to use $dispatch and $broadcast to remotely call events to a parent or child component in a parent-child nested component, so as to avoid the need to pass props or call component instance methods using refs. With that said, $dispatch and $broadcast are worth their while, and not for nothing. Once again, technology is not good or bad, only suitable or unsuitable.