Vue.js component communication is achieved through several methods, each suited to specific use cases. Below is a comprehensive overview of these methods, including code examples and their characteristics.
Props
- Direction: Parent to Child
- Purpose: Allows a parent component to pass data to a child component via attributes.
- Characteristics:
- Read-Only: Props are immutable by default in the child component.
- Validation: Props can have defined validation rules.
- Dynamic: Props can update with changes in the parent component’s state.
Parent Component (Parent.vue)
<template> <div> <ChildComponent :message="parentMessage" /> </div> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, data() { return { parentMessage: 'Hello from Parent' }; } }; </script>
Child Component (ChildComponent.vue)
<template> <div> <p>{{ message }}</p> </div> </template> <script> export default { props: { message: String } }; </script>
$emit (Events)
- Direction: Child to Parent
- Purpose: Enables a child component to notify the parent by triggering custom events.
- Characteristics:
- Data Transmission: Events can carry data payloads.
- Multiple Events: A child can emit multiple distinct events.
Child Component (ChildComponent.vue)
<template> <button @click="notifyParent">Notify Parent</button> </template> <script> export default { methods: { notifyParent() { this.$emit('child-event', 'Hello from Child'); } } }; </script>
Parent Component (Parent.vue)
<template> <ChildComponent @child-event="handleChildEvent" /> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, methods: { handleChildEvent(data) { console.log('Received data from child:', data); } } }; </script>
Vuex
- Global State Management
- Purpose: Manages application-wide state accessible by any component.
- Characteristics:
- State: Stores global state data.
- Mutations: Synchronous operations, the only way to modify state.
- Actions: Handle asynchronous operations, dispatching mutations.
- Getters: Computed properties based on state, cached for efficiency.
- Modules: Split state management into modules for large applications.
Setting Up Vuex Store (store.js)
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: { sharedCounter: 0 }, mutations: { increment(state) { state.sharedCounter++; } }, actions: { incrementAsync({ commit }) { setTimeout(() => { commit('increment'); }, 1000); } }, getters: { getCounter: state => state.sharedCounter } });
Main Application (main.js)
Ensure the application integrates the Vuex Store:
import Vue from 'vue'; import App from './App.vue'; import store from './store'; new Vue({ store, render: h => h(App) }).$mount('#app');
Using Vuex in Components (ComponentUsingVuex.vue)
<template> <div> <p>Counter: {{ counter }}</p> <button @click="increment">Increment</button> <button @click="incrementAsync">Increment Async</button> </div> </template> <script> export default { computed: { counter() { return this.$store.getters.getCounter; } }, methods: { increment() { this.$store.commit('increment'); }, incrementAsync() { this.$store.dispatch('incrementAsync'); } } }; </script>
Provide/Inject
- Cross-Level Communication
- Purpose: Allows an ancestor component to provide data that descendants can inject, bypassing intermediate parent-child hierarchies.
- Characteristics:
- Hierarchy-Independent: Works across multiple levels but may increase code coupling.
- Limited Use: Best suited for library or framework-level data passing, not general component communication.
Ancestor Component (AncestorComponent.vue)
<template> <div> <ChildComponent /> </div> </template> <script> export default { provide() { return { ancestorValue: 'Value from Ancestor' }; } }; </script>
Descendant Component (DescendantComponent.vue)
<template> <div> <p>Value from Ancestor: {{ ancestorValue }}</p> </div> </template> <script> export default { inject: ['ancestorValue'], mounted() { console.log('Injected value:', this.ancestorValue); } }; </script>
Ref and v-model
- Direct Reference
- Purpose: Allows a parent to directly access a child component instance or enable two-way data binding.
- Characteristics:
- Refs: Used to access child component instances or DOM elements for direct manipulation.
- v-model: Facilitates two-way data binding, commonly used with form elements or custom components.
Parent Component (ParentComponent.vue)
<template> <ChildComponent ref="childRef" v-model="parentValue" /> <button @click="logChildRef">Log Child Ref</button> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, data() { return { parentValue: 'Initial Value' }; }, methods: { logChildRef() { console.log(this.$refs.childRef); } } }; </script>
Child Component (ChildComponent.vue)
<template> <input :value="value" @input="$emit('input', $event.target.value)" /> </template> <script> export default { props: ['value'] }; </script>
Custom Events
- Event-Based Communication
- Purpose: Enables non-standard communication between components via custom events.
- Characteristics:
- Flexible: Can be triggered and listened to between any components.
- Complex Interactions: Suitable for specific interactions or intricate component communication.
Child Component (ChildComponent.vue)
<template> <button @click="customEvent">Send Custom Event</button> </template> <script> export default { methods: { customEvent() { this.$emit('custom-event', 'Data to send'); } } }; </script>
Parent Component (ParentComponent.vue)
<template> <ChildComponent @custom-event="handleCustomEvent" /> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, methods: { handleCustomEvent(data) { console.log('Received custom event data:', data); } } }; </script>
Slots
- Content Distribution
- Purpose: Allows a parent component to insert content into specific areas of a child component.
- Characteristics:
- Default Slot: The default content area in a child component.
- Named Slots: Multiple slots can be defined, with the parent specifying content placement.
- Scoped Slots: The parent can access child component data to customize slot content.
Parent Component (ParentComponent.vue)
<template> <WrapperComponent> <h1 slot="header">Custom Header</h1> <p slot="body">Custom Body Content</p> </WrapperComponent> </template> <script> import WrapperComponent from './WrapperComponent.vue'; export default { components: { WrapperComponent } }; </script>
Wrapper Component (WrapperComponent.vue)
<template> <div> <slot name="header"></slot> <div class="content"> <slot name="body"></slot> </div> </div> </template>
Composition API
- New Feature
- Purpose: Introduced in Vue 3, it provides better logic and data organization within components.
- Characteristics:
-
setup()
Function: Runs at the start of the component lifecycle, accessing props and lifecycle hooks. -
ref
andreactive
: Manage reactive data. -
provide
andinject
: Enhanced implementations for flexible cross-component data passing.
-
Parent Component (ParentComponent.vue)
<template> <ChildComponent :count="count" @updateCount="updateCount" /> </template> <script> import { ref, onMounted } from 'vue'; import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, setup() { const count = ref(0); function updateCount(newCount) { count.value = newCount; } onMounted(() => { console.log('Initial count:', count.value); }); return { count, updateCount }; } }; </script>
Child Component (ChildComponent.vue)
<template> <button @click="increment">Increment</button> </template> <script> import { ref } from 'vue'; export default { props: ['count'], setup(props, { emit }) { const count = ref(props.count); function increment() { count.value++; emit('updateCount', count.value); } return { count, increment }; } }; </script>
👉 Click to join and systematically improve development capabilities: Advanced Development Tutorial
Top comments (0)