Vue applications utilize several communication mechanisms between components to facilitate data flow and interaction:
1. Props and Event Emission
- Props: Parent-to-child data transmission occurs through attribute binding (
v-bindor:). Child components must declare expected properties in theirpropsoption. - Event Emission: Child components communicate with parents using
this.$emit('eventName', payload)to trigger custom events with associated data. Parents listen to these events usingv-on:eventNameor@eventNamesyntax.
2. Reference Access (refs)
Direct access to child component instances or DOM elements. When requiring child component status or method invocation, assign a ref attribute to the child component and access it via this.$refs.referenceName in the parent.
3. Global Event System
Establish an independent Vue instance as an event hub, utilizing its $on and $emit methods for cross-component communication across any hierarchy level.
4. Vuex State Management
Vuex serves as Vue's centralized state management solution, handling application-wide state sharing and providing actions and mutations for state modification and synchronization.
5. Dependency Injection (provide/inject)
Parent components can supply data through the provide option without explicit prop passing, allowing descendant components to access this data via the inject option.
6. Additional Parent-Child Communication Methods
- Passing callback functions as props, enabling child components to invoke parent methods when appropriate.
- Utilizing
v-modelfor two-way data binding in form inputs or compatible custom components.
7. Dynamic Components with State Preservation
Dynamic components combined with keep-alive preserve component states during switching, enabling indirect communication through activated/deactivated lifecycle changes.
Props and Event Emission Example
Parent Component
<template>
<div>
<child-element :greeting="masterMessage" @modify-greeting="processGreetingUpdate"></child-element>
</div>
</template>
<script>
import ChildElement from './ChildElement.vue'
export default {
components: { ChildElement },
data() {
return {
masterMessage: 'Greetings from master'
}
},
methods: {
processGreetingUpdate(updatedMessage) {
this.masterMessage = updatedMessage;
}
}
}
</script>
Child Component
<template>
<div>
{{ greeting }}
<button @click="sendUpdatedGreeting">Modify Greeting</button>
</div>
</template>
<script>
export default {
props: ['greeting'],
methods: {
sendUpdatedGreeting() {
this.$emit('modify-greeting', 'Updated greeting from child');
}
}
}
</script>
Reference Access Example
Parent Component
<template>
<div>
<child-element ref="childInstance"></child-element>
<button @click="invokeChildFunction">Invoke Child Function</button>
</div>
</template>
<script>
import ChildElement from './ChildElement.vue'
export default {
components: { ChildElement },
methods: {
invokeChildFunction() {
this.$refs.childInstance.executeChildLogic();
}
}
}
Child Component
export default {
methods: {
executeChildLogic() {
console.log('Child function executed');
}
}
}
Global Event System
// globalEvents.js
import Vue from 'vue';
export const EventHub = new Vue();
// Emitting events from any component
EventHub.$emit('specialEvent', dataPayload);
// Receiving events in any component
EventHub.$on('specialEvent', (dataPayload) => {
console.log(dataPayload);
});
Vuex Implementation
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
centralData: 'Application State'
},
mutations: {
modifyCentralData(state, newData) {
state.centralData = newData;
}
},
actions: {
changeData({ commit }, newData) {
commit('modifyCentralData', newData);
}
},
getters: {
retrieveCentralData(state) {
return state.centralData;
}
}
});
Usage in Components
import { mapState, mapActions } from 'vuex';
export default {
computed: {
...mapState(['centralData'])
},
methods: {
...mapActions(['changeData'])
},
mounted() {
this.changeData('Modified Application State');
console.log(this.centralData);
}
};
Provide/Inject Pattern
Parent Component
provide() {
return {
ancestorData: 'Supplied by Ancestor'
};
}
Child Component
inject: ['ancestorData'],
created() {
console.log(this.ancestorData); // "Supplied by Ancestor"
}
Child-to-Parenet Communication
Parent Component
<template>
<div>
<child-element :callback="refreshParentData"></child-element>
<p>Master Data: {{ masterData }}</p>
</div>
</template>
<script>
import ChildElement from './ChildElement.vue'
export default {
components: { ChildElement },
data() {
return {
masterData: ''
}
},
methods: {
refreshParentData(newData) {
this.masterData = newData;
}
}
}
</script>
Child Component
<template>
<button @click="executeParentCallback">Refresh Parent Data</button>
</template>
<script>
export default {
props: {
callback: { type: Function, required: true }
},
methods: {
executeParentCallback() {
this.callback('Fresh data from child');
}
}
}
</script>
Two-Way Binding with v-model
Parent Component
<template>
<search-input v-model="queryText" />
<p>Query Text: {{ queryText }}</p>
</template>
<script>
import SearchInput from './SearchInput.vue'
export default {
components: { SearchInput },
data() {
return {
queryText: ''
}
}
}
</script>
Child Component
<template>
<input type="text" :value="modelValue" @input="handleInput" />
</template>
<script>
export default {
model: {
prop: 'modelValue',
event: 'input'
},
props: {
modelValue: String
},
methods: {
handleInput(event) {
this.$emit('input', event.target.value);
}
}
}
</script>
Dynamic Components with Keep-Alive
<template>
<div>
<button @click="activeTab = 'first-tab'">First Tab</button>
<button @click="activeTab = 'second-tab'">Second Tab</button>
<keep-alive>
<component :is="activeTab === 'first-tab' ? FirstTab : SecondTab" @update-shared="manageSharedState" />
</keep-alive>
</div>
</template>
<script>
import FirstTab from './FirstTab.vue';
import SecondTab from './SecondTab.vue';
export default {
components: { FirstTab, SecondTab },
data() {
return {
activeTab: 'first-tab',
commonData: {}
}
},
methods: {
manageSharedState(data) {
this.commonData = data;
}
}
}
</script>
The FirstTab and SecondTab components can emit update-shared events to modify parent state. When switching back to cached components, the preserved state maintained by keep-alive continues displaying previous data or state.