阿里云主机折上折
  • 微信号
Current Site:Index > The component emits option

The component emits option

Author:Chuan Chen 阅读数:56934人阅读 分类: Vue.js

In Vue.js, the emits option is used to explicitly declare the events that a component can trigger. It helps developers better understand the component's communication methods while providing type checking and improved documentation support.

Basic Usage of the emits Option

The emits option can be an array or an object. When using an array, simply list the names of events the component may trigger:

export default {
  emits: ['submit', 'cancel'],
  methods: {
    handleSubmit() {
      this.$emit('submit', { data: 'some data' })
    }
  }
}

When using the object form, you can add validation functions for each event:

export default {
  emits: {
    submit: (payload) => {
      return typeof payload === 'object' && 'data' in payload
    }
  },
  methods: {
    handleSubmit() {
      this.$emit('submit', { data: 'valid payload' }) // Passes validation
      // this.$emit('submit', 'invalid') // Will show a warning in the console
    }
  }
}

Why Use the emits Option

  1. Better Self-Documentation: By examining the emits option, other developers can immediately see which events the component will trigger.
  2. Type Checking: In TypeScript projects, emits can work with the type system.
  3. Runtime Validation: The object form of emits can validate whether event parameters meet expectations.
  4. Vue 3 Compatibility: In Vue 3, events not declared in emits will be treated as native events bound to the component's root element.

Working with props

emits is often used in conjunction with props to achieve two-way communication between parent and child components:

export default {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  methods: {
    updateValue(newValue) {
      this.$emit('update:modelValue', newValue)
    }
  }
}

Type Support in TypeScript

In TypeScript, you can more precisely define the types of event payloads:

interface SubmitPayload {
  data: string
  timestamp: number
}

export default defineComponent({
  emits: {
    submit: (payload: SubmitPayload) => {
      return typeof payload.data === 'string' && 
             typeof payload.timestamp === 'number'
    }
  },
  methods: {
    triggerSubmit() {
      this.$emit('submit', {
        data: 'example',
        timestamp: Date.now()
      })
    }
  }
})

Naming Conventions for Custom Events

While Vue doesn't enforce this, it's recommended to follow these conventions:

  • Use kebab-case for event names (e.g., update-value).
  • Use the update: prefix for two-way binding events.
  • Avoid naming conflicts with native DOM events.
export default {
  emits: ['update:value', 'input-change'],
  methods: {
    handleInput(e) {
      this.$emit('update:value', e.target.value)
      this.$emit('input-change', e)
    }
  }
}

Advanced Usage of Event Validation

Validation functions can perform complex logic checks:

export default {
  emits: {
    upload: ({ file, size }) => {
      const isValidType = ['image/jpeg', 'image/png'].includes(file.type)
      const isValidSize = size < 1024 * 1024 // 1MB
      return isValidType && isValidSize
    }
  },
  methods: {
    handleUpload(file) {
      if (this.$emit('upload', { file, size: file.size })) {
        // Validation passed, proceed with upload
      }
    }
  }
}

Integration with the Composition API

Using emits in the setup function:

import { defineComponent } from 'vue'

export default defineComponent({
  emits: ['focus', 'submit'],
  setup(props, { emit }) {
    const handleFocus = () => {
      emit('focus', { focused: true })
    }
    
    return { handleFocus }
  }
})

Handling Undeclared Events

In Vue 3, undeclared emits behave differently:

export default {
  // No emits declared
  methods: {
    triggerEvent() {
      this.$emit('custom-event') // Treated as a native DOM event
      this.$emit('click') // Will trigger twice (once native, once custom)
    }
  }
}

Performance Considerations

While emits validation adds slight runtime overhead, it's very useful in development environments. These validation codes are removed in production builds.

Deep Integration with v-model

Vue 3 supports multiple v-model bindings, where emits plays a key role:

export default {
  props: {
    firstName: String,
    lastName: String
  },
  emits: ['update:firstName', 'update:lastName'],
  methods: {
    updateName(type, value) {
      this.$emit(`update:${type}`, value)
    }
  }
}

When using the component:

<NameComponent
  v-model:first-name="firstName"
  v-model:last-name="lastName"
/>

Event Validation in Testing

In component tests, you can verify whether events are correctly triggered:

import { mount } from '@vue/test-utils'

test('emits submit event', () => {
  const wrapper = mount(MyComponent)
  wrapper.vm.$emit('submit', { data: 'test' })
  expect(wrapper.emitted().submit[0]).toEqual([{ data: 'test' }])
})

Integration with Third-Party Libraries

When integrating third-party libraries, emits can help encapsulate native events:

export default {
  emits: ['change'],
  mounted() {
    this.thirdPartyLib.on('change', (value) => {
      this.$emit('change', value)
    })
  }
}

Alternative to the Event Bus Pattern

Although Vue 3 removed the global event bus, emits can facilitate component communication:

// Parent component
const app = createApp({
  template: `
    <child-component @notify="handleNotify" />
  `,
  methods: {
    handleNotify(message) {
      console.log(message)
    }
  }
})

// Child component
app.component('child-component', {
  emits: ['notify'],
  created() {
    this.$emit('notify', 'Hello from child')
  }
})

Dynamic Event Names

In some advanced scenarios, you might need to dynamically generate event names:

export default {
  emits: ['dynamic-event'],
  methods: {
    triggerDynamicEvent(eventName) {
      if (this.$options.emits.includes(eventName)) {
        this.$emit(eventName, 'payload')
      }
    }
  }
}

Native Browser Events vs. Custom Events

It's important to understand the difference between native DOM events and Vue custom events:

export default {
  emits: ['click'], // Custom click event
  template: `
    <button @click="$emit('click', $event)">
      This button triggers both native and custom click events
    </button>
  `
}

Destructuring Event Parameters

You can destructure event parameters when receiving them:

// Parent component
<child-component @submit="handleSubmit" />

methods: {
  handleSubmit({ data, meta }) {
    console.log(data, meta)
  }
}

// Child component
export default {
  emits: ['submit'],
  methods: {
    triggerSubmit() {
      this.$emit('submit', {
        data: 'payload',
        meta: { timestamp: Date.now() }
      })
    }
  }
}

Combining Events with Slots

Sometimes you need to pass events through slots:

// Parent component
<template v-slot:item="{ data, emitEvent }">
  <button @click="emitEvent('select', data)">
    {{ data.name }}
  </button>
</template>

// Child component
export default {
  emits: ['select'],
  template: `
    <ul>
      <li v-for="item in items" :key="item.id">
        <slot name="item" :data="item" :emitEvent="$emit" />
      </li>
    </ul>
  `
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.