Vue 3 Composition API 最佳实践
为什么选择 Composition API
Vue 3 的 Composition API 解决了 Options API 在大型组件中的代码组织问题:
- 逻辑复用:Composables 替代 Mixins
- 类型推导:天然支持 TypeScript
- 代码组织:相关逻辑集中在一起
核心 API 速查
ref vs reactive
import { ref, reactive } from 'vue';
// ref:适用于基本类型,也可包装对象
const count = ref(0);
console.log(count.value); // 0
// reactive:适用于对象,自动解包
const state = reactive({
name: '张三',
age: 25,
});
console.log(state.name); // '张三'
选择建议:
- 基本类型 →
ref - 对象/数组 →
reactive或ref(ref 会自动解包对象) - 需要整体替换 →
ref
computed
import { computed, ref } from 'vue';
const firstName = ref('张');
const lastName = ref('三');
// 计算属性
const fullName = computed(() => `${firstName.value}${lastName.value}`);
// 可写的计算属性
const fullNameWritable = computed({
get: () => `${firstName.value}${lastName.value}`,
set: (val: string) => {
firstName.value = val[0];
lastName.value = val.slice(1);
},
});
watch & watchEffect
import { watch, watchEffect, ref } from 'vue';
const keyword = ref('');
// watch:明确指定要监听的数据
watch(keyword, (newVal, oldVal) => {
console.log(`搜索词从 "${oldVal}" 变为 "${newVal}"`);
}, { debounce: 300 }); // 需要自定义 debounce
// watchEffect:自动追踪依赖
watchEffect(() => {
// 自动收集 keyword 作为依赖
fetchSearchResults(keyword.value);
});
自定义 Composables
// composables/useFetch.ts
import { ref, watchEffect, toValue, type Ref } from 'vue';
interface UseFetchReturn<T> {
data: Ref<T | null>;
error: Ref<string | null>;
loading: Ref<boolean>;
}
export function useFetch<T>(url: string | Ref<string>): UseFetchReturn<T> {
const data = ref<T | null>(null) as Ref<T | null>;
const error = ref<string | null>(null);
const loading = ref(false);
watchEffect(async () => {
loading.value = true;
try {
const response = await fetch(toValue(url));
if (!response.ok) throw new Error(`HTTP ${response.status}`);
data.value = await response.json();
} catch (e) {
error.value = (e as Error).message;
} finally {
loading.value = false;
}
});
return { data, error, loading };
}
使用:
<script setup lang="ts">
import { useFetch } from '@/composables/useFetch';
const { data: posts, loading, error } = useFetch<Post[]>('/api/posts');
</script>
常见陷阱
1. reactive 解构丢失响应性
const state = reactive({ count: 0 });
// 错误:解构后丢失响应性
const { count } = state;
// 正确:使用 toRefs
import { toRefs } from 'vue';
const { count } = toRefs(state);
2. ref 在模板中自动解包
<template>
<!-- 正确:模板中自动解包,不需要 .value -->
<p>{{ count }}</p>
</template>
总结
Composition API 不仅是一种新的写法,更是更好的代码组织方式。合理使用 Composables,可以让你的 Vue 代码更清晰、更易维护。