1. Composables
// useCounter.ts
import { ref } from 'vue'
export function useCounter() {
// State is isolated per component instance
const count = ref(0)
const increment = () => count.value++
const decrement = () => count.value--
return {
count,
increment,
decrement
}
}
// Usage in components
const Component1 = {
setup() {
const { count, increment } = useCounter() // Instance 1
return { count, increment }
}
}
const Component2 = {
setup() {
const { count, increment } = useCounter() // Instance 2 (separate state)
return { count, increment }
}
}
Characteristics:
Best for:
// Example: Form handling composable
export function useForm() {
const formData = ref({})
const errors = ref({})
const validate = () => {
// Validation logic
}
const submit = async () => {
// Submit logic
}
return {
formData,
errors,
validate,
submit
}
}
// Example: API fetching composable
export function useApi<T>(url: string) {
const data = ref<T | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
const fetch = async () => {
loading.value = true
try {
data.value = await axios.get(url)
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
return {
data,
loading,
error,
fetch
}
}
2. Store (Pinia)
// store/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
history: [] as number[]
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
this.history.push(this.count)
}
}
})
// Usage across components
const Component1 = {
setup() {
const store = useCounterStore() // Same instance
return { count: store.count }
}
}
const Component2 = {
setup() {
const store = useCounterStore() // Same instance
return { count: store.count }
}
}
Characteristics: