Vue Components
Building blocks of your Vue application
What are Components?
Components are reusable Vue instances with custom names that encapsulate markup, logic, and styles.
Vue 3 Components: Built using the Composition API or Options API. Composition API is recommended for new projects.
Component APIs
Composition API (Recommended)
Modern, flexible approach
- Better TypeScript support
- More flexible code organization
- Easier logic reuse
- Smaller bundle size
Options API
Traditional Vue 2 style
- Familiar structure
- Good for simple components
- Still fully supported
- Easier for beginners
Single-File Components (SFC)
Vue's special file format that encapsulates template, script, and style in one file.
Composition API Example
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
</script>
<template>
<div class="counter">
<p>Count: {{ count }}</p>
<p>Doubled: {{ doubled }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<style scoped>
.counter {
padding: 20px;
border: 1px solid #42b883;
border-radius: 8px;
}
button {
background-color: #42b883;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
Options API Example
<script>
export default {
data() {
return {
count: 0
}
},
computed: {
doubled() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
<template>
<div class="counter">
<p>Count: {{ count }}</p>
<p>Doubled: {{ doubled }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<style scoped>
.counter {
padding: 20px;
border: 1px solid #42b883;
}
</style>
Props
Props are custom attributes for passing data from parent to child components.
Composition API Props
<script setup>
// Define props with TypeScript
const props = defineProps({
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
},
tags: {
type: Array,
default: () => []
}
})
// Access props
console.log(props.title)
</script>
<template>
<div>
<h2>{{ title }}</h2>
<p>Count: {{ count }}</p>
<ul>
<li v-for="tag in tags" :key="tag">{{ tag }}</li>
</ul>
</div>
</template>
<!-- Usage -->
<MyComponent
title="Hello"
:count="5"
:tags="['vue', 'javascript']"
/>
TypeScript Props
<script setup lang="ts">
interface Props {
title: string
count?: number
tags?: string[]
}
const props = withDefaults(defineProps<Props>(), {
count: 0,
tags: () => []
})
</script>
Custom Events
Emit custom events to communicate from child to parent components.
Emitting Events
<script setup>
// Define emits
const emit = defineEmits(['update', 'delete', 'custom'])
function handleClick() {
// Emit event with payload
emit('update', { id: 1, value: 'new value' })
}
function handleDelete(id) {
emit('delete', id)
}
</script>
<template>
<button @click="handleClick">Update</button>
<button @click="handleDelete(123)">Delete</button>
</template>
<!-- Parent component -->
<MyComponent
@update="handleUpdate"
@delete="handleDelete"
/>
V-model with Components
<script setup>
// Child component
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
function updateValue(newValue) {
emit('update:modelValue', newValue)
}
</script>
<template>
<input
:value="modelValue"
@input="updateValue($event.target.value)"
/>
</template>
<!-- Parent usage -->
<script setup>
import { ref } from 'vue'
const text = ref('')
</script>
<template>
<CustomInput v-model="text" />
<p>{{ text }}</p>
</template>
Slots
Slots allow you to pass template content to child components.
Default Slot
<!-- Card.vue -->
<template>
<div class="card">
<div class="card-header">
<slot name="header">Default Header</slot>
</div>
<div class="card-body">
<slot>Default content</slot>
</div>
<div class="card-footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<!-- Usage -->
<Card>
<template #header>
<h3>My Card Title</h3>
</template>
<p>This is the card content</p>
<template #footer>
<button>Save</button>
</template>
</Card>
Scoped Slots
<!-- List.vue -->
<script setup>
const props = defineProps(['items'])
</script>
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" :index="index">
{{ item.name }}
</slot>
</li>
</ul>
</template>
<!-- Usage -->
<List :items="users">
<template #default="{ item, index }">
<strong>{{ index + 1 }}.</strong> {{ item.name }}
</template>
</List>
Lifecycle Hooks
Lifecycle hooks allow you to run code at specific stages of a component's life.
Composition API Hooks
<script setup>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
onBeforeMount(() => {
console.log('Before mount')
})
onMounted(() => {
console.log('Component mounted')
// Fetch data, setup listeners
})
onBeforeUpdate(() => {
console.log('Before update')
})
onUpdated(() => {
console.log('Component updated')
})
onBeforeUnmount(() => {
console.log('Before unmount')
// Cleanup listeners
})
onUnmounted(() => {
console.log('Component unmounted')
})
</script>
Lifecycle Diagram
| Hook | When it runs | Common uses |
|---|---|---|
onBeforeMount |
Before component is mounted | Final changes before rendering |
onMounted |
After component is mounted | API calls, DOM manipulation |
onBeforeUpdate |
Before reactive data changes | Access old DOM state |
onUpdated |
After DOM re-renders | Access updated DOM |
onBeforeUnmount |
Before component unmounts | Cleanup, save data |
onUnmounted |
After component unmounts | Final cleanup |