Skip to content

Latest commit

 

History

History
134 lines (95 loc) · 4.64 KB

0000-emits-option.md

File metadata and controls

134 lines (95 loc) · 4.64 KB
  • Start Date: 2019-02-27
  • Target Major Version: 2.x & 3.x
  • Reference Issues: N/A
  • Implementation PR:

Summary

Make it explicit what events are emitted by the component.

Basic example

const Comp = {
  emits: {
    submit: payload => {
      // validate payload by returning a boolean
    }
  },

  created() {
    this.$emit('submit', {
      /* payload */
    })
  }
}

Motivation

  • 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.

  • Runtime Validation: The option also offers a way to perform runtime validation of emitted event payloads.

  • Type Inference: The emits option can be used to provide type inference so that this.$emit and setupContext.emit calls can be typed.

  • IDE Support: IDEs can leverage the emits option to provide auto-completion when using v-on listeners on a component.

  • 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.

Detailed design

A new optional component option named emits is introduced.

Array Syntax

For simple use cases, the option value can be an Array containing string events names:

{
  emits: [
    'eventA',
    'eventB'
  }
}

Object Syntax

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
      }
    }
  }
}

Fallthrough Control

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 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.

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.

Type Inference

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!
    }
  }
})

Drawbacks

  • 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 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.

Adoption strategy

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:

  1. Provide a codemod that automatically scans all instances of $emit calls in a component and generate the emits option.

  2. (Opt-in) Emit a runtime warning when an emitted event isn't explicitly declared using the option.