You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
					
						
							7.1 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							7.1 KiB
						
					
					
				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?
- Better TypeScript Support: Full type checking of parameters and return values
- Superior IDE Navigation: Ctrl+click takes you directly to implementation
- Explicit Contracts: Clear declaration of what functions a component needs
- Easier Testing: Simple to mock and test in isolation
- Flexibility: Can pass any function, not just event handlers
When to Use $emit
- DOM-like Events: @click,@input,@submit,@change
- Lifecycle Events: @mounted,@before-unmount,@updated
- Form Validation: @validation-error,@validation-success
- Event Bubbling: When events need to bubble through multiple components
- Vue DevTools Integration: When you want events visible in DevTools timeline
Implementation Patterns
Function Props Pattern
// Child Component
@Component({
  name: "MyComponent"
})
export default class MyComponent extends Vue {
  @Prop({ required: true }) onSave!: (data: SaveData) => Promise<void>;
  @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();
  }
}
<!-- Parent Template -->
<MyComponent
  :on-save="handleSave"
  :on-cancel="handleCancel"
  :on-validate="validateForm"
/>
$emit Pattern (for DOM-like events)
// Child Component
@Component({
  name: "FormComponent"
})
export default class FormComponent extends Vue {
  @Emit("submit")
  handleSubmit() {
    return this.formData;
  }
  @Emit("input")
  handleInput(value: string) {
    return value;
  }
}
<!-- Parent Template -->
<FormComponent
  @submit="handleFormSubmit"
  @input="handleInputChange"
/>
Automatic Code Generation Guidelines
Component Template Generation
When generating component templates, follow these patterns:
Function Props Template
<template>
  <div class="component-name">
    <!-- Component content -->
    <button @click="handleAction">
      {{ buttonText }}
    </button>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-facing-decorator";
@Component({
  name: "ComponentName"
})
export default class ComponentName extends Vue {
  @Prop({ required: true }) onAction!: () => void;
  @Prop({ required: true }) buttonText!: string;
  @Prop({ required: false }) disabled?: boolean;
  handleAction() {
    if (!this.disabled) {
      this.onAction();
    }
  }
}
</script>
$emit Template (for DOM events)
<template>
  <div class="component-name">
    <!-- Component content -->
    <button @click="handleClick">
      {{ buttonText }}
    </button>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Emit } from "vue-facing-decorator";
@Component({
  name: "ComponentName"
})
export default class ComponentName extends Vue {
  @Prop({ required: true }) buttonText!: string;
  @Prop({ required: false }) disabled?: boolean;
  @Emit("click")
  handleClick() {
    return { disabled: this.disabled };
  }
}
</script>
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:
// Action-oriented names
onSave: (data: SaveData) => Promise<void>
onDelete: (id: string) => Promise<void>
onUpdate: (item: Item) => void
onValidate: (data: FormData) => boolean
onNavigate: (route: string) => void
$emit Events:
// 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
// Define reusable function types
interface SaveHandler {
  (data: SaveData): Promise<void>;
}
interface ValidationHandler {
  (data: FormData): boolean;
}
// Use in components
@Prop({ required: true }) onSave!: SaveHandler;
@Prop({ required: true }) onValidate!: ValidationHandler;
Event Types
// 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
// 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
// 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
- Identify business logic events (not DOM events)
- Add function props to component interface
- Update parent components to pass functions
- Remove $emit decorators and event handlers
- Update tests to use function mocks
Example Migration
Before ($emit):
@Emit("save")
handleSave() {
  return this.formData;
}
After (Function Props):
@Prop({ required: true }) onSave!: (data: FormData) => void;
handleSave() {
  this.onSave(this.formData);
}
Best Practices Summary
- Use function props for business logic, data operations, and complex interactions
- Use $emit for DOM-like events, lifecycle events, and simple user interactions
- Be consistent within your codebase
- Document your patterns for team alignment
- Consider TypeScript when choosing between approaches
- Test both patterns appropriately
Code Generation Templates
Component Generator Input
interface ComponentSpec {
  name: string;
  props: Array<{
    name: string;
    type: string;
    required: boolean;
    isFunction: boolean;
  }>;
  events: Array<{
    name: string;
    payloadType?: string;
  }>;
  template: string;
}
Generated Output
// 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.