Skip to content

Commit cfc67ef

Browse files
authored
Add "emits" component option (#16)
1 parent 1d49580 commit cfc67ef

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed

active-rfcs/0000-emits-option.md

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
- Start Date: 2019-02-27
2+
- Target Major Version: 2.x & 3.x
3+
- Reference Issues: N/A
4+
- Implementation PR:
5+
6+
# Summary
7+
8+
Make it explicit what events are emitted by the component.
9+
10+
# Basic example
11+
12+
```javascript
13+
const Comp = {
14+
emits: {
15+
submit: payload => {
16+
// validate payload by returning a boolean
17+
}
18+
},
19+
20+
created() {
21+
this.$emit('submit', {
22+
/* payload */
23+
})
24+
}
25+
}
26+
```
27+
28+
# Motivation
29+
30+
- **Documentation:** Similar to `props`, explicit `emits` declaration serves as self-documenting code. This can be useful for other developers to instantly understand what events the component is supposed to emit.
31+
32+
- **Runtime Validation:** The option also offers a way to perform runtime validation of emitted event payloads.
33+
34+
- **Type Inference:** The `emits` option can be used to provide type inference so that `this.$emit` and `setupContext.emit` calls can be typed.
35+
36+
- **IDE Support:** IDEs can leverage the `emits` option to provide auto-completion when using `v-on` listeners on a component.
37+
38+
- **Listener Fallthrough Control:** With the proposed attribute fallthrough changes, `v-on` listeners on components will fallthrough as native listeners by default. `emits` provides a way to declare events as component-only to avoid unnecessary registration of native listeners.
39+
40+
# Detailed design
41+
42+
A new optional component option named `emits` is introduced.
43+
44+
## Array Syntax
45+
46+
For simple use cases, the option value can be an Array containing string events names:
47+
48+
```javascript
49+
{
50+
emits: [
51+
'eventA',
52+
'eventB'
53+
}
54+
}
55+
```
56+
57+
## Object Syntax
58+
59+
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.
60+
61+
```javascript
62+
{
63+
emits: {
64+
// no validation
65+
click: null,
66+
67+
// with validation
68+
//
69+
submit: payload => {
70+
if (payload.email && payload.password) {
71+
return true
72+
} else {
73+
console.warn(`Invalid submit event payload!`)
74+
return false
75+
}
76+
}
77+
}
78+
}
79+
```
80+
81+
## Fallthrough Control
82+
83+
The new [Attribute Fallthrough Behavior](https://github.com/vuejs/rfcs/blob/amend-optional-props/active-rfcs/0000-attr-fallthrough.md) proposed in [#154](https://github.com/vuejs/rfcs/pull/154) now applies automatic fallthrough for `v-on` listeners used on a component:
84+
85+
```html
86+
<Foo @click="onClick" />
87+
```
88+
89+
We are not making `emits` required for `click` to be trigger-able by component emitted events for backwards compatibility. Therefore in the above exampe, 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`.
90+
91+
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.
92+
93+
Event listeners declared by `emits` are also excluded from `this.$attrs` of the component.
94+
95+
## Type Inference
96+
97+
The Object validator syntax was picked with TypeScript type inference in mind. The validator type signature can be used to type `$emit` calls:
98+
99+
```ts
100+
const Foo = defineComponent({
101+
emits: {
102+
submit: (payload: { email: string; password: string }) => {
103+
// perform runtime validation
104+
}
105+
},
106+
107+
methods: {
108+
onSubmit() {
109+
this.$emit('submit', {
110+
111+
password: 123 // Type error!
112+
})
113+
114+
this.$emit('non-declared-event') // Type error!
115+
}
116+
}
117+
})
118+
```
119+
120+
# Drawbacks
121+
122+
- 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.
123+
124+
- 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 `props` and `emits` options to the Array format in production builds. This way the dev only code is stripped but the runtime behavior will stay consistent.
125+
126+
# Adoption strategy
127+
128+
The introduction of the `emits` option should not break any existing usage of `$emit`.
129+
130+
However, with the fallthrough behavior change, it would be ideal to always declare emitted events. We can:
131+
132+
1. Provide a codemod that automatically scans all instances of `$emit` calls in a component and generate the `emits` option.
133+
134+
2. (Opt-in) Emit a runtime warning when an emitted event isn't explicitly declared using the option.

0 commit comments

Comments
 (0)