Vue 3 Composition API Best Practices

作者:

在

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, ref is 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:

  1. Input unwrapping: When accepting ref params, use toValue() internally to support ref/getter/primitive alike.
  2. Side-effect cleanup: Clean up listeners and timers in onScopeDispose.
  3. 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>() and defineEmits<T>() for props/emits.
  • For template refs, use useTemplateRef or a generic ref<InstanceType<Comp> | null>(null).
  • Use the ComponentExposed library for component instance types.

6. Five Common Anti-Patterns

  • Destructuring reactive loses reactivity: Wrap with toRefs after 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.

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注