The Composition API is a powerful new way to organize component logic in Vue 3, providing better TypeScript support and more flexible code reuse.
Why Composition API?
The Composition API addresses the limitations of the Options API, especially for larger components:
// Options API - Logic can be scattered
export default {
data() { return { count: 0 } },
methods: { increment() { this.count++ } },
computed: { double() { return this.count * 2 } }
}
// Composition API - Logic is organized by feature
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(0)
const double = computed(() => count.value * 2)
const increment = () => count.value++
return { count, double, increment }
}
}Script Setup Syntax
The <script setup> syntax is the recommended way to use the Composition API:
<script setup>
import { ref, computed, onMounted } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() {
count.value++
}
onMounted(() => {
console.log('Component mounted!')
})
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ double }}</p>
<button @click="increment">Increment</button>
</div>
</template>Reactive References
ref()
Used for primitive values and objects:
import { ref } from 'vue'
const count = ref(0)
const user = ref({ name: 'John' })
// Access value with .value
count.value++
user.value.name = 'Jane'reactive()
Used for objects (no .value needed):
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: { name: 'John' }
})
state.count++
state.user.name = 'Jane'Computed Properties
Create reactive computed values:
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// Writable computed
const fullNameWritable = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (value) => {
[firstName.value, lastName.value] = value.split(' ')
}
})Watchers
watch()
Watch specific sources:
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
// Watch multiple sources
watch([count, anotherRef], ([newCount, newAnother]) => {
console.log('Values changed:', newCount, newAnother)
})watchEffect()
Automatically track dependencies:
import { ref, watchEffect } from 'vue'
const count = ref(0)
watchEffect(() => {
console.log(`Count is: ${count.value}`)
})Lifecycle Hooks
Composition API lifecycle hooks:
import {
onMounted,
onBeforeMount,
onUpdated,
onBeforeUpdate,
onUnmounted,
onBeforeUnmount
} from 'vue'
onMounted(() => {
console.log('Component mounted')
})
onUpdated(() => {
console.log('Component updated')
})
onUnmounted(() => {
console.log('Component unmounted')
})Composables
Reusable logic functions:
// composables/useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
function decrement() { count.value-- }
function reset() { count.value = initialValue }
return {
count,
double,
increment,
decrement,
reset
}
}
// Using the composable
<script setup>
import { useCounter } from './composables/useCounter'
const { count, double, increment } = useCounter(10)
</script>Props and Emits
<script setup>
const props = defineProps({
title: String,
count: {
type: Number,
default: 0
}
})
const emit = defineEmits(['update', 'delete'])
function handleClick() {
emit('update', props.count + 1)
}
</script>Provide/Inject
// Parent
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
// Child
import { inject } from 'vue'
const theme = inject('theme')Best Practices
- 1Use
ref()for primitives andreactive()for objects - 2Extract reusable logic into composables
- 3Use computed properties for derived state
- 4Watchers should be used for side effects
- 5Keep setup functions focused and readable
The Composition API provides a more flexible and maintainable way to organize component logic, especially as applications grow in complexity.