# Component Communication Guide ## Overview This guide establishes our preferred patterns for component communication in Vue.js applications, with a focus on maintainability, type safety, and developer experience. ## Core Principle: Function Props Over $emit **Preference**: Use function props for business logic and data operations, reserve $emit for DOM-like events. ### Why Function Props? 1. **Better TypeScript Support**: Full type checking of parameters and return values 2. **Superior IDE Navigation**: Ctrl+click takes you directly to implementation 3. **Explicit Contracts**: Clear declaration of what functions a component needs 4. **Easier Testing**: Simple to mock and test in isolation 5. **Flexibility**: Can pass any function, not just event handlers ### When to Use $emit 1. **DOM-like Events**: `@click`, `@input`, `@submit`, `@change` 2. **Lifecycle Events**: `@mounted`, `@before-unmount`, `@updated` 3. **Form Validation**: `@validation-error`, `@validation-success` 4. **Event Bubbling**: When events need to bubble through multiple components 5. **Vue DevTools Integration**: When you want events visible in DevTools timeline ## Implementation Patterns ### Function Props Pattern ```typescript // Child Component @Component({ name: "MyComponent" }) export default class MyComponent extends Vue { @Prop({ required: true }) onSave!: (data: SaveData) => Promise; @Prop({ required: true }) onCancel!: () => void; @Prop({ required: false }) onValidate?: (data: FormData) => boolean; async handleSave() { const data = this.collectFormData(); await this.onSave(data); } handleCancel() { this.onCancel(); } } ``` ```vue ``` ### $emit Pattern (for DOM-like events) ```typescript // Child Component @Component({ name: "FormComponent" }) export default class FormComponent extends Vue { @Emit("submit") handleSubmit() { return this.formData; } @Emit("input") handleInput(value: string) { return value; } } ``` ```vue ``` ## Automatic Code Generation Guidelines ### Component Template Generation When generating component templates, follow these patterns: #### Function Props Template ```vue ``` #### $emit Template (for DOM events) ```vue ``` ### Code Generation Rules #### 1. Function Props for Business Logic - **Data operations**: Save, delete, update, validate - **Navigation**: Route changes, modal opening/closing - **State management**: Store actions, state updates - **API calls**: Data fetching, form submissions #### 2. $emit for User Interactions - **Click events**: Button clicks, link navigation - **Form events**: Input changes, form submissions - **Lifecycle events**: Component mounting, unmounting - **UI events**: Focus, blur, scroll, resize #### 3. Naming Conventions **Function Props:** ```typescript // Action-oriented names onSave: (data: SaveData) => Promise onDelete: (id: string) => Promise onUpdate: (item: Item) => void onValidate: (data: FormData) => boolean onNavigate: (route: string) => void ``` **$emit Events:** ```typescript // Event-oriented names @click: (event: MouseEvent) => void @input: (value: string) => void @submit: (data: FormData) => void @focus: (event: FocusEvent) => void @mounted: () => void ``` ### TypeScript Integration #### Function Prop Types ```typescript // Define reusable function types interface SaveHandler { (data: SaveData): Promise; } interface ValidationHandler { (data: FormData): boolean; } // Use in components @Prop({ required: true }) onSave!: SaveHandler; @Prop({ required: true }) onValidate!: ValidationHandler; ``` #### Event Types ```typescript // Define event payload types interface ClickEvent { target: HTMLElement; timestamp: number; } @Emit("click") handleClick(): ClickEvent { return { target: this.$el, timestamp: Date.now() }; } ``` ## Testing Guidelines ### Function Props Testing ```typescript // Easy to mock and test const mockOnSave = jest.fn(); const wrapper = mount(MyComponent, { propsData: { onSave: mockOnSave } }); await wrapper.vm.handleSave(); expect(mockOnSave).toHaveBeenCalledWith(expectedData); ``` ### $emit Testing ```typescript // Requires event simulation const wrapper = mount(MyComponent); await wrapper.find('button').trigger('click'); expect(wrapper.emitted('click')).toBeTruthy(); ``` ## Migration Strategy ### From $emit to Function Props 1. **Identify business logic events** (not DOM events) 2. **Add function props** to component interface 3. **Update parent components** to pass functions 4. **Remove $emit decorators** and event handlers 5. **Update tests** to use function mocks ### Example Migration **Before ($emit):** ```typescript @Emit("save") handleSave() { return this.formData; } ``` **After (Function Props):** ```typescript @Prop({ required: true }) onSave!: (data: FormData) => void; handleSave() { this.onSave(this.formData); } ``` ## Best Practices Summary 1. **Use function props** for business logic, data operations, and complex interactions 2. **Use $emit** for DOM-like events, lifecycle events, and simple user interactions 3. **Be consistent** within your codebase 4. **Document your patterns** for team alignment 5. **Consider TypeScript** when choosing between approaches 6. **Test both patterns** appropriately ## Code Generation Templates ### Component Generator Input ```typescript interface ComponentSpec { name: string; props: Array<{ name: string; type: string; required: boolean; isFunction: boolean; }>; events: Array<{ name: string; payloadType?: string; }>; template: string; } ``` ### Generated Output ```typescript // Generator should automatically choose function props vs $emit // based on the nature of the interaction (business logic vs DOM event) ``` This guide ensures consistent, maintainable component communication patterns across the application.