From 471ee5e53eddb882ea131e9911754247f4f5e4ed Mon Sep 17 00:00:00 2001 From: skirtle <65301168+skirtles-code@users.noreply.github.com> Date: Mon, 5 Jul 2021 09:39:31 +0100 Subject: [PATCH] docs: add various improvements to the Events API migration guide --- src/guide/migration/events-api.md | 59 +++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/src/guide/migration/events-api.md b/src/guide/migration/events-api.md index fd3afa994f..60feb5e1f7 100644 --- a/src/guide/migration/events-api.md +++ b/src/guide/migration/events-api.md @@ -7,46 +7,46 @@ badges: ## Overview -`$on`, `$off` and `$once` instance methods are removed. Application instances no longer implement the event emitter interface. +`$on`, `$off` and `$once` instance methods are removed. Component instances no longer implement the event emitter interface. ## 2.x Syntax -In 2.x, Vue instance could be used to trigger handlers attached imperatively via the event emitter API (`$on`, `$off` and `$once`). This was used to create _event hubs_ to create global event listeners used across the whole application: +In 2.x, a Vue instance could be used to trigger handlers attached imperatively via the event emitter API (`$on`, `$off` and `$once`). This could be used to create an _event bus_ to create global event listeners used across the whole application: ```js -// eventHub.js +// eventBus.js -const eventHub = new Vue() +const eventBus = new Vue() -export default eventHub +export default eventBus ``` ```js // ChildComponent.vue -import eventHub from './eventHub' +import eventBus from './eventBus' export default { mounted() { - // adding eventHub listener - eventHub.$on('custom-event', () => { + // adding eventBus listener + eventBus.$on('custom-event', () => { console.log('Custom event triggered!') }) }, beforeDestroy() { - // removing eventHub listener - eventHub.$off('custom-event') + // removing eventBus listener + eventBus.$off('custom-event') } } ``` ```js // ParentComponent.vue -import eventHub from './eventHub' +import eventBus from './eventBus' export default { methods: { callGlobalCustomEvent() { - eventHub.$emit('custom-event') // if ChildComponent is mounted, we will have a message in the console + eventBus.$emit('custom-event') // if ChildComponent is mounted, we will have a message in the console } } } @@ -54,30 +54,51 @@ export default { ## 3.x Update -We removed `$on`, `$off` and `$once` methods from the instance completely. `$emit` is still a part of the existing API as it's used to trigger event handlers declaratively attached by a parent component +We removed `$on`, `$off` and `$once` methods from the instance completely. `$emit` is still a part of the existing API as it's used to trigger event handlers declaratively attached by a parent component. ## Migration Strategy -In Vue 3, it is no longer possible to use these APIs to listen to a component's own emitted events from within a component, there is no migration path for that use case. +[Migration build flag: `INSTANCE_EVENT_EMITTER`](migration-build.html#compat-configuration) + +In Vue 3, it is no longer possible to use these APIs to listen to a component's own emitted events from within a component. There is no migration path for that use case. + +### Root Component Events + +Static event listeners can be added to the root component by passing them as props to `createApp`: + +```js +createApp(App, { + // Listen for the 'expand' event + onExpand() { + console.log('expand') + } +}) +``` -But the eventHub pattern can be replaced by using an external library implementing the event emitter interface, for example [mitt](https://github.com/developit/mitt) or [tiny-emitter](https://github.com/scottcorgan/tiny-emitter). +### Event Bus + +The event bus pattern can be replaced by using an external library implementing the event emitter interface, for example [mitt](https://github.com/developit/mitt) or [tiny-emitter](https://github.com/scottcorgan/tiny-emitter). Example: ```js -//eventHub.js +// eventBus.js import emitter from 'tiny-emitter/instance' export default { $on: (...args) => emitter.on(...args), $once: (...args) => emitter.once(...args), $off: (...args) => emitter.off(...args), - $emit: (...args) => emitter.emit(...args), + $emit: (...args) => emitter.emit(...args) } ``` This provides the same event emitter API as in Vue 2. -These methods may also be supported in a future compatibility build of Vue 3. +In most circumstances, using a global event bus for communicating between components is discouraged. While it is often the simplest solution in the short term, it almost invariably proves to be a maintenance headache in the long term. Depending on the circumstances, there are various alternatives to using an event bus: -[Migration build flag: `INSTANCE_EVENT_EMITTER`](migration-build.html#compat-configuration) +* [Props](/guide/component-basics.html#passing-data-to-child-components-with-props) and [events](/guide/component-basics.html#listening-to-child-components-events) should be your first choice for parent-child communication. Siblings can communicate via their parent. +* [Provide and inject](/guide/component-provide-inject.html) allow a component to communicate with its slot contents. This is useful for tightly-coupled components that are always used together. +* `provide`/`inject` can also be used for long-distance communication between components. It can help to avoid 'prop drilling', where props need to be passed down through many levels of components that don't need those props themselves. +* Prop drilling can also be avoided by refactoring to use slots. If an interim component doesn't need the props then it might indicate a problem with separation of concerns. Introducing a slot in that component allows the parent to create the content directly, so that props can be passed without the interim component needing to get involved. +* [Global state management](/guide/state-management.html), such as [Vuex](https://next.vuex.vuejs.org/).