Vue 3 Composition API实战指南:从Options到Composition

Vue 3 Composition API简介

Vue 3引入了Composition API,这是对传统Options API的重大改进。Composition API提供了更灵活、更强大的代码组织方式,特别适合大型应用和逻辑复用。

1. Options API vs Composition API

传统Options API示例

// Options API写法
export default {
    data() {
        return {
            count: 0,
            message: "Hello Vue",
            todos: []
        };
    },
    computed: {
        doubleCount() {
            return this.count * 2;
        },
        filteredTodos() {
            return this.todos.filter(todo => !todo.completed);
        }
    },
    methods: {
        increment() {
            this.count++;
        },
        addTodo(text) {
            this.todos.push({ id: Date.now(), text, completed: false });
        }
    },
    mounted() {
        console.log("组件已挂载");
        this.fetchTodos();
    },
    watch: {
        count(newVal, oldVal) {
            console.log(`count从${oldVal}变为${newVal}`);
        }
    }
};

Composition API示例

// Composition API写法
import { ref, computed, watch, onMounted } from "vue";

export default {
    setup() {
        // 响应式数据
        const count = ref(0);
        const message = ref("Hello Vue");
        const todos = ref([]);
        
        // 计算属性
        const doubleCount = computed(() => count.value * 2);
        const filteredTodos = computed(() => 
            todos.value.filter(todo => !todo.completed)
        );
        
        // 方法
        const increment = () => {
            count.value++;
        };
        
        const addTodo = (text) => {
            todos.value.push({ 
                id: Date.now(), 
                text, 
                completed: false 
            });
        };
        
        const fetchTodos = async () => {
            try {
                const response = await fetch("/api/todos");
                todos.value = await response.json();
            } catch (error) {
                console.error("获取待办事项失败:", error);
            }
        };
        
        // 监听器
        watch(count, (newVal, oldVal) => {
            console.log(`count从${oldVal}变为${newVal}`);
        });
        
        // 生命周期钩子
        onMounted(() => {
            console.log("组件已挂载");
            fetchTodos();
        });
        
        // 返回模板可用的数据和方法
        return {
            count,
            message,
            todos,
            doubleCount,
            filteredTodos,
            increment,
            addTodo
        };
    }
};

2. 核心响应式API详解

ref和reactive

import { ref, reactive, toRefs } from "vue";

// ref用于基本类型
const count = ref(0);
const name = ref("张三");

// reactive用于对象
const user = reactive({
    id: 1,
    name: "李四",
    email: "lisi@example.com",
    profile: {
        age: 25,
        address: "北京"
    }
});

// 访问ref的值需要使用.value
console.log(count.value); // 0
count.value++; // 修改值

// reactive对象直接访问属性
console.log(user.name); // 李四
user.name = "王五";

// 使用toRefs将reactive对象转换为ref集合
const { id, name: userName, email } = toRefs(user);
console.log(userName.value); // 王五

computed和watch

import { ref, computed, watch, watchEffect } from "vue";

const price = ref(100);
const quantity = ref(2);
const discount = ref(0.1);

// 计算属性
const total = computed(() => {
    return price.value * quantity.value * (1 - discount.value);
});

const formattedTotal = computed(() => {
    return `¥${total.value.toFixed(2)}`;
});

// 监听单个响应式数据
watch(price, (newPrice, oldPrice) => {
    console.log(`价格从${oldPrice}变为${newPrice}`);
    // 可以执行副作用,如发送分析事件
    trackPriceChange(newPrice);
});

// 监听多个数据源
watch([price, quantity], ([newPrice, newQuantity], [oldPrice, oldQuantity]) => {
    console.log(`价格: ${oldPrice}→${newPrice}, 数量: ${oldQuantity}→${newQuantity}`);
});

// 立即执行的监听器
watchEffect(() => {
    console.log(`当前总价: ${formattedTotal.value}`);
    // 自动追踪所有依赖的响应式数据
});

// 深度监听对象
const user = reactive({ info: { name: "张三" } });
watch(
    () => user.info,
    (newInfo, oldInfo) => {
        console.log("用户信息变化:", newInfo);
    },
    { deep: true }
);

3. 逻辑复用:自定义Composition函数

// useCounter.js - 计数器逻辑复用
import { ref, computed } from "vue";

export function useCounter(initialValue = 0, step = 1) {
    const count = ref(initialValue);
    
    const increment = () => {
        count.value += step;
    };
    
    const decrement = () => {
        count.value -= step;
    };
    
    const reset = () => {
        count.value = initialValue;
    };
    
    const double = computed(() => count.value * 2);
    const isEven = computed(() => count.value % 2 === 0);
    
    return {
        count,
        increment,
        decrement,
        reset,
        double,
        isEven
    };
}

// useFetch.js - 数据获取逻辑复用
import { ref, onMounted } from "vue";

export function useFetch(url, options = {}) {
    const data = ref(null);
    const error = ref(null);
    const loading = ref(false);
    
    const fetchData = async () => {
        loading.value = true;
        error.value = null;
        
        try {
            const response = await fetch(url, options);
            if (!response.ok) {
                throw new Error(`HTTP错误: ${response.status}`);
            }
            data.value = await response.json();
        } catch (err) {
            error.value = err;
            console.error("获取数据失败:", err);
        } finally {
            loading.value = false;
        };
    };
    
    // 自动执行(可选)
    if (options.autoFetch !== false) {
        onMounted(fetchData);
    }
    
    return {
        data,
        error,
        loading,
        fetchData,
        refetch: fetchData
    };
}

// 在组件中使用
import { useCounter, useFetch } from "./composables";

export default {
    setup() {
        const { count, increment, double } = useCounter(0, 1);
        const { data: posts, loading, error } = useFetch("/api/posts");
        
        return {
            count,
            increment,
            double,
            posts,
            loading,
            error
        };
    }
};

4. 高级特性:Provide/Inject和Teleport

Provide/Inject实现依赖注入

// 父组件提供数据
import { provide, reactive } from "vue";

export default {
    setup() {
        const user = reactive({
            id: 1,
            name: "张三",
            role: "admin"
        });
        
        const theme = reactive({
            mode: "dark",
            primaryColor: "#1890ff",
            fontSize: 14
        });
        
        // 提供数据给子孙组件
        provide("user", user);
        provide("theme", theme);
        provide("updateTheme", (newTheme) => {
            Object.assign(theme, newTheme);
        });
        
        return { user, theme };
    }
};

// 子组件注入数据
import { inject } from "vue";

export default {
    setup() {
        const user = inject("user");
        const theme = inject("theme");
        const updateTheme = inject("updateTheme");
        
        const toggleTheme = () => {
            updateTheme({
                mode: theme.mode === "dark" ? "light" : "dark"
            });
        };
        
        return { user, theme, toggleTheme };
    }
};

5. 性能优化和最佳实践

响应式数据优化

import { ref, shallowRef, markRaw } from "vue";

// 使用shallowRef避免深度响应式(性能优化)
const largeObject = shallowRef({
    // 大型对象,内部属性变化不会触发响应式更新
    data: /* 大量数据 */
});

// 标记对象为非响应式
const utilityObject = markRaw({
    // 工具对象,不需要响应式
    formatDate,
    validateEmail,
    constants: { API_URL, TIMEOUT }
});

// 避免在模板中频繁创建新对象
const user = reactive({
    name: "张三",
    age: 25
});

// 不好:每次渲染都创建新对象
const userInfo = computed(() => ({
    name: user.name,
    age: user.age
}));

// 好:使用toRefs保持引用
const { name, age } = toRefs(user);

组件设计模式

// 1. 单一职责原则
// 每个composition函数只做一件事
export function useFormValidation() { /* 表单验证逻辑 */ }
export function useApiRequest() { /* API请求逻辑 */ }
export function useLocalStorage() { /* 本地存储逻辑 */ }

// 2. 可测试性
// composition函数易于单元测试
import { useCounter } from "./useCounter";

describe("useCounter", () => {
    it("should increment count", () => {
        const { count, increment } = useCounter(0);
        increment();
        expect(count.value).toBe(1);
    });
});

// 3. 类型安全(TypeScript)
import { Ref } from "vue";

export interface User {
    id: number;
    name: string;
    email: string;
}

export function useUser(): {
    user: Ref;
    loading: Ref;
    fetchUser: (id: number) => Promise;
} {
    const user = ref(null);
    const loading = ref(false);
    
    const fetchUser = async (id: number) => {
        // 类型安全的实现
    };
    
    return { user, loading, fetchUser };
}

总结

Vue 3 Composition API带来了更灵活、更强大的代码组织方式:

  1. 更好的逻辑复用:通过自定义composition函数实现逻辑复用
  2. 更清晰的代码组织:相关逻辑可以组织在一起,而不是分散在各个选项中
  3. 更好的TypeScript支持:完整的类型推断和类型安全
  4. 更小的包体积:Tree-shaking友好,只导入需要的API
  5. 更好的性能:响应式系统重写,性能提升显著

对于新项目,建议直接使用Composition API。对于现有项目,可以逐步迁移,两者可以共存。

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容