前言
Vue3 的 Composition API 是对 Options API 的范式升级。它解决了大型组件中逻辑复用和组织的问题,让代码更灵活、更可维护。
一、响应式基础
1.1 ref vs reactive
import { ref, reactive, toRefs } from "vue";
// ref:适用于基本类型
const count = ref(0);
count.value++;
// reactive:适用于对象
const state = reactive({
name: "张三",
age: 25,
});
// reactive 的陷阱:解构会失去响应性
const { name } = state; // name 不再是响应式
// 解决方案:toRefs
const { name: reactiveName } = toRefs(state);1.2 computed
import { ref, computed } from "vue";
const firstName = ref("张");
const lastName = ref("三");
// 只读计算属性
const fullName = computed(() => `${firstName.value}${lastName.value}`);
// 可写计算属性
const fullNameWritable = computed({
get() {
return `${firstName.value}${lastName.value}`;
},
set(newValue: string) {
firstName.value = newValue[0];
lastName.value = newValue.slice(1);
},
});二、watch 和 watchEffect
2.1 watch
import { ref, watch } from "vue";
const keyword = ref("");
// 监听单个 ref
watch(keyword, (newVal, oldVal) => {
console.log(`关键词从 ${oldVal} 变为 ${newVal}`);
});
// 监听多个源
watch([keyword, category], ([newKeyword, newCategory]) => {
search(newKeyword, newCategory);
});
// 深度监听对象
watch(
() => state,
(newState) => {
saveToStorage(newState);
},
{ deep: true }
);2.2 watchEffect
import { ref, watchEffect } from "vue";
const userId = ref(1);
// 自动追踪依赖,立即执行
watchEffect(() => {
fetchUser(userId.value); // userId 变化时自动重新执行
});三、自定义 Hook(组合式函数)
3.1 useMousePosition
// composables/useMousePosition.ts
import { ref, onMounted, onUnmounted } from "vue";
export function useMousePosition() {
const x = ref(0);
const y = ref(0);
function update(e: MouseEvent) {
x.value = e.pageX;
y.value = e.pageY;
}
onMounted(() => {
window.addEventListener("mousemove", update);
});
onUnmounted(() => {
window.removeEventListener("mousemove", update);
});
return { x, y };
}使用:
<script setup lang="ts">
import { useMousePosition } from "@/composables/useMousePosition";
const { x, y } = useMousePosition();
</script>
<template>
<p>鼠标位置: {{ x }}, {{ y }}</p>
</template>3.2 useFetch
// composables/useFetch.ts
import { ref, watchEffect } from "vue";
export function useFetch<T>(url: () => string) {
const data = ref<T | null>(null);
const error = ref<string | null>(null);
const loading = ref(false);
watchEffect(async () => {
loading.value = true;
error.value = null;
try {
const res = await fetch(url());
if (!res.ok) throw new Error(`HTTP ${res.status}`);
data.value = await res.json() as T;
} catch (e) {
error.value = (e as Error).message;
} finally {
loading.value = false;
}
});
return { data, error, loading };
}四、生命周期钩子
import {
onMounted, onUpdated, onUnmounted,
onBeforeMount, onBeforeUpdate, onBeforeUnmount
} from "vue";
onBeforeMount(() => { /* 组件挂载前 */ });
onMounted(() => { /* DOM 已挂载 */ });
onBeforeUpdate(() => { /* 响应式数据变更,DOM 更新前 */ });
onUpdated(() => { /* DOM 更新后 */ });
onBeforeUnmount(() => { /* 组件卸载前 */ });
onUnmounted(() => { /* 组件已卸载 */ });五、总结
Composition API 的核心优势在于逻辑复用和代码组织。通过自定义 Hook,可以将组件逻辑提取为可复用的函数,告别 Options API 时代 mixin 的命名冲突和数据来源不透明问题。建议在项目中优先使用 <script setup> 语法,配合 TypeScript 获得最佳开发体验。
评论 (0)