Vue 2 Component Architecture: Registration, Lifecycle, and State Communication
Every Vue component resides in a .vue file, encapsulating markup, logic, and styles.
<template>
<div class="main-wrapper"></div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
/* Scoped styles prevent leakage */
</style>
The <template> acts as an invisible wrapper and permits only a single root element. The <script> exports an object containing data, methods, and configurations. The <style> supports pre-processors and scoping via the scoped attribute.
Component Registration
Local Registration: Ideal for components used sparingly. Import the component and declare it within the components option.
<template>
<div>
<NavBar />
<SidePanel />
</div>
</template>
<script>
import NavBar from '@/components/NavBar.vue';
import SidePanel from '@/components/SidePanel.vue';
export default {
components: {
NavBar,
SidePanel
}
}
</script>
Global Registration: Suitable for frequently reused components. Define them once in the entry point (main.js).
import Vue from 'vue';
import App from './App.vue';
import GlobalButton from '@/components/GlobalButton.vue';
Vue.component('BaseButton', GlobalButton);
new Vue({
render: h => h(App)
}).$mount('#app');
Usage remains standard: <BaseButton />.
Custom Attributes (Props)
Props enable component reusability by allowing data to be passed from outside. They are strictly read-only; to manipulate a prop, transfer its value to a reactive data property.
Array Syntax: Lacks type checking and default values.
export default {
props: ['initialValue', 'label']
}
Object Syntax: Provides validation, types, and defaults.
export default {
props: {
label: {
type: String,
default: 'Default Label',
required: true
},
initialValue: {
type: Number,
default: 0
}
}
}
When passing dynamic values, use v-bind. For instance, :count="10" passes a number, whereas count="10" passes a literal string.
Style Scoping and Deep Selectors
Adding the scoped attribute ensures styles apply only to the current component by adding unique data attributes to DOM elements. To affect child component styles from a parent, use deep selectors like ::v-deep.
<style lang="scss" scoped>
::v-deep .child-element {
color: crimson;
}
</style>
Component Lifecycle
A component undergoes creation, mounting, updating, and destruction. Lifecycle hooks are built-in methods triggered automatically during these phases.
export default {
created() {
console.log('Component instance created');
}
}
Data Communication
Parent to Child: Data flows downwrad via props. The parent binds data to the child's custom attribute.
Parent Component:
<template>
<ProfileCard :greeting="welcomeMessage" :userData="currentUser" />
</template>
<script>
import ProfileCard from '@/components/ProfileCard.vue';
export default {
data() {
return {
welcomeMessage: 'Hello World',
currentUser: { name: 'Alice', role: 'Admin' }
};
},
components: { ProfileCard }
}
</script>
Child Component (ProfileCard.vue):
<template>
<div>{{ greeting }} - {{ userData.name }}</div>
</template>
<script>
export default {
props: {
greeting: { type: String, required: true },
userData: { type: Object, required: true }
}
}
</script>
Child to Parent: Data flows upward via custom events using $emit.
Child Component:
<template>
<button @click="incrementCounter">Increment</button>
</template>
<script>
export default {
data() {
return { counter: 0 };
},
methods: {
incrementCounter() {
this.counter += 1;
this.$emit('countUpdated', this.counter);
}
}
}
</script>
Parent Component:
<template>
<div>
<span>Latest: {{ latestCount }}</span>
<ProfileCard @countUpdated="handleCountChange" />
</div>
</template>
<script>
import ProfileCard from '@/components/ProfileCard.vue';
export default {
data() {
return { latestCount: 0 };
},
components: { ProfileCard },
methods: {
handleCountChange(newValue) {
this.latestCount = newValue;
}
}
}
</script>
Sibling Communication: For non-hierarchical components, an EventBus facilitates communication.
- Create an event hub (
eventHub.js):
import Vue from 'vue';
export default new Vue();
- Sender dispatches events via
$emit:
<template>
<button @click="broadcastData">Send Data</button>
</template>
<script>
import hub from './eventHub.js';
export default {
data() {
return { payload: 'Data from sibling' };
},
methods: {
broadcastData() {
hub.$emit('dataTransmitted', this.payload);
}
}
}
</script>
- Receiver listens via
$on(typically inside thecreatedhook):
<template>
<div>{{ receivedData }}</div>
</template>
<script>
import hub from './eventHub.js';
export default {
data() {
return { receivedData: '' };
},
created() {
hub.$on('dataTransmitted', (val) => {
this.receivedData = val;
});
}
}
</script>