- Start Date: 2019-02-27
- Target Major Version: 2.x & 3.x
- Reference Issues: N/A
- Implementation PR:
Make it explicit what events are emitted by the component.
const Comp = {
emits: {
submit: payload => {
// validate payload by returning a boolean
}
},
created() {
this.$emit('submit', {
/* payload */
})
}
}-
Documentation: Similar to
props, explicitemitsdeclaration serves as self-documenting code. This can be useful for other developers to instantly understand what events the component is supposed to emit. -
Runtime Validation: The option also offers a way to perform runtime validation of emitted event payloads.
-
Type Inference: The
emitsoption can be used to provide type inference so thatthis.$emitandsetupContext.emitcalls can be typed. -
IDE Support: IDEs can leverage the
emitsoption to provide auto-completion when usingv-onlisteners on a component. -
Listener Fallthrough Control: With the proposed attribute fallthrough changes,
v-onlisteners on components will fallthrough as native listeners by default.emitsprovides a way to declare events as component-only to avoid unnecessary registration of native listeners.
A new optional component option named emits is introduced.
For simple use cases, the option value can be an Array containing string events names:
{
emits: [
'eventA',
'eventB'
}
}Or it can be an object with event names as its keys. The value of each property can either be null or a validator function. The validation function will receive the additional arguments passed to the $emit call. For example, if this.$emit('foo', 1, 2) is called, the corresponding validator for foo will receive the arguments 1, 2. The validator function should return a boolean to indicate whether the event arguments are valid.
{
emits: {
// no validation
click: null,
// with validation
//
submit: payload => {
if (payload.email && payload.password) {
return true
} else {
console.warn(`Invalid submit event payload!`)
return false
}
}
}
}The new Attribute Fallthrough Behavior proposed in #154 now applies automatic fallthrough for v-on listeners used on a component:
<Foo @click="onClick" />We are not making emits required for click to be trigger-able by component emitted events for backwards compatibility. Therefore in the above example, without the emits option, the listener can be triggered by both a native click event on Foo's root element, or a custom click event emitted by Foo.
If, however, click is declared as a custom event by using the emits option, it will then only be triggered by custom events and will no longer fallthrough as a native listener.
Event listeners declared by emits are also excluded from this.$attrs of the component.
The Object validator syntax was picked with TypeScript type inference in mind. The validator type signature can be used to type $emit calls:
const Foo = defineComponent({
emits: {
submit: (payload: { email: string; password: string }) => {
// perform runtime validation
}
},
methods: {
onSubmit() {
this.$emit('submit', {
email: '[email protected]',
password: 123 // Type error!
})
this.$emit('non-declared-event') // Type error!
}
}
})-
The option requires some extra effort for all components that emit custom events. However, it is technically optional, and the benefits should outweigh the extra effort needed.
-
Runtime validations should only be performed in dev mode but can potentially bloat production bundle size. Props validators have the same issue. Both can be solved with a Babel plugin that transforms
propsandemitsoptions to the Array format in production builds. This way the dev only code is stripped but the runtime behavior will stay consistent.
The introduction of the emits option should not break any existing usage of $emit.
However, with the fallthrough behavior change, it would be ideal to always declare emitted events. We can:
-
Provide a codemod that automatically scans all instances of
$emitcalls in a component and generate theemitsoption. -
(Opt-in) Emit a runtime warning when an emitted event isn't explicitly declared using the option.