Vue3 Composition API 深度解析:从 ref 到自定义 Hook

Vue3 Composition API 深度解析:从 ref 到自定义 Hook

admin
2026-03-05 / 0 评论 / 16 阅读

前言

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

评论 (0)

取消
0:00