Mitt-based type security event bus implementation in Vue3
Preface
In the era of Vue3's Composition API, communication between components has become more flexible and diverse. Although props/emit, provide/inject, etc. can meet most scenarios, EventBus is still an elegant solution in some cross-component and cross-layer communication scenarios. This article will show you how to implement a type-safe event bus in your Vue3 project.
Why Choose MITT
Variations of Vue2 vs Vue3
In Vue2, we can use a Vue instance as an event bus:
javascript
Vue2 Way (Deprecated)
const eventBus = new Vue()
But in Vue3, this is no longer available. We need to find new solutions.
Advantages of mitt
MITT is a lightweight event emitter library with the following features:
- Small size: Only 200 bytes, which barely affects the package volume
- Type Safety: Full TypeScript support
- API Concise: Only three core methods: on, off, and emit
- Excellent Performance: Implemented based on Map, excellent performance
Implement type safe event buses
1. Define the event type
typescript
type Events = {
'user-logout': void;
'user-login': void;
'search-tag': string;
};
Benefits of Type Definition:
- Compilation-time type checking
- IDE Smart Prompts
- Prevent misspellings of event names
- Clarify the event parameter type
2. Create an event bus instance
typescript
import mitt from 'mitt';
export const eventBus = mitt<Events>();
3. Define event constants
typescript
export const EventTypes = {
USER_LOGOUT: 'user-logout' as const,
USER_LOGIN: 'user-login' as const,
SEARCH_TAG: 'search-tag' as const,
};
Advantages of Using Constants:
- Avoid hard-coding strings
- Easy to refactor and maintain
- Provide code hints
- Unified incident management
Practical Application Scenarios
1. Notification of user status changes
typescript
Send a logout event
eventBus.emit(EventTypes.USER_LOGOUT);
Listen for logout events
eventBus.on(EventTypes.USER_LOGOUT, () => {
Clean up user-related data
Redirects to the login page
A logout prompt is displayed
});
2. Search tag pass
typescript
Send a search tag
eventBus.emit(EventTypes.SEARCH_TAG, 'Vue3');
Listen for search events
eventBus.on(EventTypes.SEARCH_TAG, (tag: string) => {
Execute the search logic
performSearch(tag);
});
3. Topic switching notifications
typescript
Theme switching components
const toggleTheme = () => {
store.setTheme();
eventBus.emit('theme-changed', store.theme);
};
Other components listen for topic changes
eventBus.on('theme-changed', (isDark: boolean) => {
Update component styles
updateComponentTheme(isDark);
});
Use in Vue Components
1. Used in composable APIs
typescript
import { onMounted, onUnmounted } from 'vue';
import { eventBus, EventTypes } from '@/utils/eventBus';
export default {
setup() {
const handleUserLogout = () => {
Handle logout logic
console.log ('User logged out');
};
onMounted(() => {
Register for event listening
eventBus.on(EventTypes.USER_LOGOUT, handleUserLogout);
});
onUnmounted(() => {
Clean up incident monitoring
eventBus.off(EventTypes.USER_LOGOUT, handleUserLogout);
});
return {
Expose the method of sending events
logout: () => eventBus.emit(EventTypes.USER_LOGOUT)
};
}
};
2. Custom Hook Packages
typescript
import { onUnmounted } from 'vue';
import { eventBus } from '@/utils/eventBus';
export function useEventBus() {
const listeners: Array<() => void> = [];
const on = <T extends keyof Events>(
event: T,
handler: (data: Events[T]) => void
) => {
eventBus.on(event, handler);
listeners.push(() => eventBus.off(event, handler));
};
const emit = <T extends keyof Events>(
event: T,
data: Events[T]
) => {
eventBus.emit(event, data);
};
Automatically cleans up all listeners when components are uninstalled
onUnmounted(() => {
listeners.forEach(cleanup => cleanup());
});
return { on, emit };
}
3. Hook usage example
typescript
export default {
setup() {
const { on, emit } = useEventBus();
Listening to events (automatic cleanup)
on(EventTypes.USER_LOGOUT, () => {
console.log('User logged out');
});
Send an event
const handleLogout = () => {
emit(EventTypes.USER_LOGOUT);
};
return { handleLogout };
}
};
Best Practices
1. Event naming conventions
- Use kebab-case naming
- Verb-noun structure: 'user-login', 'data-updated'
- Avoid overly generic names: 'change', 'update'
2. Type definition specifications
typescript
type Events = {
No parameter events
'app-ready': void;
Base type parameters
'user-id-changed': number;
'message-sent': string;
Object type parameters
'user-updated': {
id: number;
name: string;
email: string;
};
Union type parameters
'status-changed': 'loading' | 'success' | 'error';
};
3. Memory leak protection
typescript
Example of error: Forgetting to clean up the listener
eventBus.on('some-event', handler); It can cause a memory leak
Correct example: clean up in time
onUnmounted(() => {
eventBus.off('some-event', handler);
});
4. Debugging and logging
typescript
Event logs are added in the development environment
if (import.meta.env.DEV) {
const originalEmit = eventBus.emit;
eventBus.emit = (event, data) => {
console.log(`[EventBus] ${event}:`, data);
return originalEmit.call(eventBus, event, data);
};
}
Performance Optimization
1. Event listener management
- Clean up unwanted monitors in a timely manner
- Avoid repeatedly registering listeners in a loop
- Use weak references to avoid memory leaks
2. Event frequency control
typescript
import { debounce } from 'lodash-es';
Stabilization handles high-frequency events
const debouncedHandler = debounce((data) => {
Processing logic
}, 300);
eventBus.on('scroll-event', debouncedHandler);
Comparison with other communication methods
EventBus vs Props/Emit
- EventBus: Suitable for cross-layer, loosely coupled communication
- Props/Emit: Suitable for direct communication between parent and child components
EventBus vs Provide/Inject
- EventBus: Global events, any component can listen to them
- Provide/Inject: Hierarchical dependencies, suitable for dependency injection
EventBus vs Pinia/Vuex
- EventBus: Lightweight event communication
- State Management: Complex state management and persistence
Considerations
1. Avoid abuse
EventBus, while flexible, should not be the preferred solution for component communication. Priority:
- Props/Emit (Father-Child Communication)
- Provide/Inject
- State Management (Complex State)
2. Event naming conflicts
- Use namespaces: 'user:login', 'article:created'
- Establish event naming conventions
- Clean up unwanted events regularly
3. Difficulty in debugging
- Add event logs
- Use Vue DevTools
- Create event documentation
Summary
The mitt-based event bus provides a lightweight, type-safe component communication solution for the Vue3 project. With proper type definitions, specification usage, and appropriate performance optimizations, we can build an event communication system that is both flexible and maintainable.
Core Benefits:
- Type Safety: Full TypeScript support
- Lightweight and Efficient: Extremely small size and excellent performance
- Simple to use: Clear API and good development experience
- Easy Maintenance: Standardized event management and automated cleanup mechanisms
In real-world projects, EventBus should be used as a complementary option in the component communication toolbox, working with other communication methods to build high-quality Vue3 applications.
*Event bus is a powerful tool for component communication, but the key lies in rational use and standardized management. *