Vue 3 Composition API: A Complete Guide
Vue 3 Composition API: A Complete Guide
Vue 3 introduced the Composition API, a revolutionary new way to write Vue components. This guide will walk you through everything you need to know to master this powerful feature.
What is the Composition API?
The Composition API is a set of APIs that allow you to compose component logic using imported functions instead of declaring options. It provides better TypeScript support, better code organization, and more flexible code reuse patterns.
Getting Started
First, let's see how the traditional Options API compares to the Composition API:
Options API (Vue 2)
export default {
data() {
return {
count: 0,
name: 'Vue'
}
},
computed: {
doubleCount() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
console.log('Component mounted')
}
}
Composition API (Vue 3)
<script setup>
import { ref, computed, onMounted } from 'vue'
const count = ref(0)
const name = ref('Vue')
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
onMounted(() => {
console.log('Component mounted')
})
</script>
Reactivity Fundamentals
ref
ref is used for primitive values:
import { ref } from 'vue'
const count = ref(0)
const message = ref('Hello')
// Access with .value
console.log(count.value) // 0
count.value++
reactive
reactive is used for objects:
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'John',
age: 30
}
})
// No .value needed
state.count++
Computed Properties
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
get() {
return `${firstName.value} ${lastName.value}`
},
set(newValue) {
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
Watchers
<script setup>
import { ref, watch, watchEffect } from 'vue'
const question = ref('')
const answer = ref('')
// Watch a specific ref
watch(question, async (newQuestion) => {
if (newQuestion.includes('?')) {
answer.value = 'Thinking...'
answer.value = await fetchAnswer(newQuestion)
}
})
// watchEffect for automatic dependency tracking
watchEffect(() => {
console.log(`Question is: ${question.value}`)
})
</script>
Lifecycle Hooks
<script setup>
import {
onMounted,
onUpdated,
onUnmounted,
onBeforeMount,
onBeforeUpdate,
onBeforeUnmount
} from 'vue'
onMounted(() => {
console.log('Component mounted')
})
onUpdated(() => {
console.log('Component updated')
})
onUnmounted(() => {
console.log('Component unmounted')
})
</script>
Composables: The New Mixins
One of the biggest advantages of the Composition API is composables:
// composables/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useMouse() {
const x = ref(0)
const y = ref(0)
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
return { x, y }
}
<script setup>
import { useMouse } from './composables/useMouse'
const { x, y } = useMouse()
</script>
<template>
<p>Mouse position: {{ x }}, {{ y }}</p>
</template>
Fetching Data with Composables
// composables/useFetch.js
import { ref } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(true)
fetch(url)
.then(res => res.json())
.then(json => {
data.value = json
loading.value = false
})
.catch(err => {
error.value = err
loading.value = false
})
return { data, error, loading }
}
Provide/Inject
<!-- Parent Component -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
</script>
<!-- Child Component -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
</script>
Best Practices
- Use
reffor primitives,reactivefor objects - Group related logic together - no more jumping between data, methods, computed
- Extract reusable logic into composables
- Use
<script setup>for cleaner syntax - Name composables with
useprefix
Conclusion
The Composition API represents a significant evolution in Vue.js development. It provides better code organization, improved TypeScript support, and more flexible code reuse patterns. Start using it in your next Vue 3 project and experience the difference!