Vue 3’s Composition API has become the default for new projects. But there’s a layer of best practice between “works” and “works well.” This article compiles practical experience with the Composition API across state, reuse, performance, and TypeScript.
1. Why the Composition API?
The Options API organizes code by data/methods/computed, but a single logical concern gets split across multiple options. The core value of the Composition API is organizing code by logical concern, keeping related code together.
Comparing the same “search + filter” logic:
| Dimension | Options API | Composition API |
|---|---|---|
| Organization | By option | By concern |
| Logic reuse | Mixins, prone to conflicts | Composables, clear |
| Type inference | Weak | TS-friendly |
| Tree-shaking | Poor | Excellent |
2. ref vs reactive: When to Use Which
This is the most common confusion for beginners. A practical rule:
- Primitives: use
ref. - Objects/arrays: can use
reactive, but if you’ll replace the whole value or need to destructure and pass it,refis safer. - When unsure, always
ref—lowest mental overhead.
A typical combined-state example:
import { ref, reactive, computed } from 'vue'
export function useTodo() {
// Primitives use ref
const filter = ref('all')
// Non-replaced objects use reactive
const state = reactive({
todos: [],
loading: false,
})
const visible = computed(() => {
if (filter.value === 'done') return state.todos.filter(t => t.done)
return state.todos
})
return { filter, state, visible }
}
3. Logic Reuse: Composable Best Practices
The Composition API’s most powerful capability is reusable composables. Name them starting with use; inputs are refs or primitives, outputs are refs and methods. Key points:
- Input unwrapping: When accepting ref params, use
toValue()internally to support ref/getter/primitive alike. - Side-effect cleanup: Clean up listeners and timers in
onScopeDispose. - Don’t call business components in composables: Keep pure logic; leave UI to components.
A standard composable template:
import { ref, onScopeDispose } from 'vue'
export function useMousePosition() {
const x = ref(0)
const y = ref(0)
const onMove = (e) => { x.value = e.clientX; y.value = e.clientY }
window.addEventListener('mousemove', onMove)
// Auto-clean side effects
onScopeDispose(() => window.removeEventListener('mousemove', onMove))
return { x, y }
}
4. Performance: computed, watch, and shallow
- Prefer computed: Derived values use computed for auto-caching; avoid complex expressions in templates.
- Avoid watch overuse: If computable can solve it, don’t use watch; watch is for side effects.
- Large objects use shallowRef: When deep data won’t trigger updates, shallowRef is faster than deep reactivity.
5. TypeScript Integration
The Composition API pairs excellently with TS. Adding types to a composable:
interface Todo { id: number; title: string; done: boolean }
export function useTodo(initial: Todo[] = []) {
const todos = ref(initial)
const add = (title: string) => {
todos.value.push({ id: Date.now(), title, done: false })
}
return { todos, add }
}
A few TS-friendly habits:
- Use
defineProps<T>()anddefineEmits<T>()for props/emits. - For template refs, use
useTemplateRefor a genericref<InstanceType<Comp> | null>(null). - Use the
ComponentExposedlibrary for component instance types.
6. Five Common Anti-Patterns
- Destructuring reactive loses reactivity: Wrap with
toRefsafter destructuring. - Synchronously mutating the source in watch: Causes infinite triggers.
- Putting all state in a store: Keep local state in components; use Pinia only for cross-layer.
- Directly manipulating DOM in a composable: Violates separation of concerns.
- Forgetting to clean side effects: Listener/timer leaks wreck performance after repeated component toggling.
7. Summary
The Composition API isn’t the opposite of Options; it’s a toolset that makes Vue more maintainable in complex scenarios. Using it well comes down to three things: organize by concern, reuse via composables, and guard types with TS. When you stop agonizing over “which option does this logic go in” and can write a clear composable for each concern, Vue 3’s full dividends truly unlock.

发表回复