# Component Communication in Element UI Using the Emitter Mixin: Dispatch and Broadcast Explained

> Master Element UI component communication with the emitter mixin. Learn how dispatch and broadcast efficiently manage parent-child interactions without a global event bus.

- Repository: [饿了么前端/element](https://github.com/ElemeFE/element)
- Tags: internals
- Published: 2026-03-07

---

**The Element UI emitter mixin enables Vue components to communicate bidirectionally through the component tree using `dispatch` for upward propagation to ancestors and `broadcast` for downward propagation to descendants, eliminating the need for a global event bus.**

The ElemeFE/element repository implements a lightweight communication layer for Vue components through the **emitter mixin**, which provides targeted event propagation without relying on a global event bus. This pattern allows child components to notify specific ancestors and parent components to signal specific descendants using the `componentName` convention. Understanding this mechanism is essential for debugging form validation, menu interactions, and other complex component relationships in Element UI.

## How the Emitter Mixin Enables Tree-Based Communication

The emitter mixin lives in [`src/mixins/emitter.js`](https://github.com/ElemeFE/element/blob/main/src/mixins/emitter.js) and adds two instance methods to Vue components. Both methods rely on a **convention**: each component that participates in communication defines a static `componentName` option (for example, `name: 'ElFormItem'`).

**`dispatch(componentName, eventName, params)`** travels **upward** from a child to its nearest matching ancestor by walking the `$parent` chain. When it finds a component whose `componentName` matches the target, it calls `$emit` on that ancestor.

**`broadcast(componentName, eventName, params)`** travels **downward** from a parent to all matching descendants by recursively traversing `$children`. When a descendant matches the `componentName`, the mixin emits the event on that component and continues recursion for non-matching branches.

## Core Implementation in src/mixins/emitter.js

The implementation in [`src/mixins/emitter.js`](https://github.com/ElemeFE/element/blob/main/src/mixins/emitter.js) consists of a mixin object containing the public methods and an internal recursive helper for broadcast.

The `dispatch` method (lines 14–27) climbs the component tree until it locates the requested ancestor:

```javascript
// src/mixins/emitter.js
export default {
  methods: {
    dispatch(componentName, eventName, params) {
      var parent = this.$parent || this.$root;
      var name = parent.$options.componentName;

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;
        if (parent) name = parent.$options.componentName;
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },

    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

```

The `broadcast` helper function (lines 1–11) handles downward propagation by iterating through `$children` and recursing when names do not match:

```javascript
function broadcast(componentName, eventName, params) {
  this.$children.forEach(child => {
    const name = child.$options.componentName;
    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}

```

## Real-World Usage Examples

Element UI uses the emitter mixin extensively for form validation, menu state management, and tree operations.

### Form Validation: ElInput Dispatching to ElFormItem

In [`packages/input/src/input.vue`](https://github.com/ElemeFE/element/blob/main/packages/input/src/input.vue), input components notify their parent form items about value changes to trigger validation. The `ElInput` component mixes in the emitter and calls `dispatch` to reach the nearest `ElFormItem`:

```javascript
// packages/input/src/input.vue
export default {
  name: 'ElInput',
  mixins: [Emitter],
  methods: {
    handleInput(val) {
      this.dispatch('ElFormItem', 'el.form.change', [val]);
    }
  }
};

```

This call climbs the `$parent` chain until it finds an ancestor with `componentName: 'ElFormItem'`, then emits `el.form.change` on that instance.

### Menu State Management: ElMenu Broadcasting to ElSubmenu

In [`packages/menu/src/menu.vue`](https://github.com/ElemeFE/element/blob/main/packages/menu/src/menu.vue), the root menu component broadcasts collapse state changes to all submenu descendants. The `ElMenu` component uses `broadcast` to reach every `ElSubmenu` instance simultaneously:

```javascript
// packages/menu/src/menu.vue
export default {
  name: 'ElMenu',
  mixins: [Emitter],
  methods: {
    toggleAllSubs(value) {
      this.broadcast('ElSubmenu', 'toggle-collapse', value);
    }
  }
};

```

This recursion traverses the entire subtree, emitting `toggle-collapse` on each descendant named `ElSubmenu` regardless of nesting depth.

## Setting Up Event Listeners

Components that receive emitter events register listeners using Vue's `$on` method, typically in the `mounted` lifecycle hook. The event name must match the string passed to `dispatch` or `broadcast`.

For example, `ElFormItem` listens for validation triggers from child inputs:

```javascript
export default {
  name: 'ElFormItem',
  mounted() {
    this.$on('el.form.change', this.handleChange);
    this.$on('el.form.blur', this.handleBlur);
  },
  methods: {
    handleChange(val) {
      this.validate('change');
    }
  }
};

```

This pattern creates a direct communication channel between specific component types without polluting the global event namespace.

## Summary

- The **emitter mixin** in [`src/mixins/emitter.js`](https://github.com/ElemeFE/element/blob/main/src/mixins/emitter.js) provides `dispatch` for upward communication and `broadcast` for downward communication within the Vue component tree.
- Both methods rely on the **`componentName`** option to locate target components, not the standard `name` property.
- **`dispatch`** walks the `$parent` chain to find the nearest matching ancestor, while **`broadcast`** recursively traverses `$children` to find all matching descendants.
- Element UI uses this pattern for critical features like **form validation** (`ElInput` to `ElFormItem`) and **menu state** (`ElMenu` to `ElSubmenu`).
- Listeners register via **`$on`** using the exact event name passed to the emitter methods.

## Frequently Asked Questions

### What is the difference between dispatch and broadcast in the Element UI emitter mixin?

**`dispatch`** sends events upward through the component tree to the nearest ancestor matching the specified `componentName`, while **`broadcast`** sends events downward to all descendants matching that name. Dispatch stops at the first match, making it ideal for parent-child pairs like form inputs and form items. Broadcast reaches every matching descendant at any depth, suitable for global state updates like menu collapse.

### How does the emitter mixin identify the correct target component?

The mixin uses the **`componentName`** option defined in a component's `$options`, not the standard `name` property. When calling `dispatch('ElFormItem', ...)`, the mixin checks `parent.$options.componentName === 'ElFormItem'` while traversing the tree. Components must explicitly set this option (for example, `name: 'ElFormItem'` in the component definition) to participate in emitter communication.

### Where is the emitter mixin defined in the Element UI codebase?

The core logic resides in **[`src/mixins/emitter.js`](https://github.com/ElemeFE/element/blob/main/src/mixins/emitter.js)**, which exports a mixin object containing the `dispatch` and `broadcast` methods. Components throughout the library—such as those in [`packages/input/src/input.vue`](https://github.com/ElemeFE/element/blob/main/packages/input/src/input.vue) and [`packages/menu/src/menu.vue`](https://github.com/ElemeFE/element/blob/main/packages/menu/src/menu.vue)—import and apply this mixin to enable tree-based communication.

### Why does Element UI use the emitter mixin instead of a global event bus?

The emitter mixin provides **scoped communication** that travels only through relevant component subtrees, preventing accidental cross-talk between unrelated component instances. By using explicit `componentName` targeting, the pattern maintains clear relationships between communicators and avoids the maintenance overhead and memory leak risks associated with global event buses in large component hierarchies.