引言
Vue 3的Composition API是Vue生态系统中最重要的变革之一。它不仅改变了我们编写Vue组件的方式,更重要的是改变了我们的思维方式。本文将深入探讨Composition API的核心概念、优势以及如何从Options API平滑过渡到Composition API。
1. Options API vs Composition API
首先,让我们通过一个简单的计数器组件来对比两种API的差异:
// Options API (Vue 2风格)
export default {
data() {
return {
count: 0,
message: "Hello Vue!"
};
},
computed: {
doubled() {
return this.count * 2;
}
},
methods: {
increment() {
this.count++;
},
reset() {
this.count = 0;
}
},
mounted() {
console.log("组件已挂载");
}
};
// Composition API (Vue 3风格)
import { ref, computed, onMounted } from "vue";
export default {
setup() {
// 响应式数据
const count = ref(0);
const message = ref("Hello Vue!");
// 计算属性
const doubled = computed(() => count.value * 2);
// 方法
const increment = () => {
count.value++;
};
const reset = () => {
count.value = 0;
};
// 生命周期钩子
onMounted(() => {
console.log("组件已挂载");
});
// 返回模板可用的数据和方法
return {
count,
message,
doubled,
increment,
reset
};
}
};
2. Composition API核心概念
2.1 ref 和 reactive
import { ref, reactive, computed, watch } from "vue";
export default {
setup() {
// ref - 用于基本类型和对象引用
const count = ref(0);
const user = ref({ name: "John", age: 30 });
// reactive - 用于对象(深度响应式)
const state = reactive({
todos: [
{ id: 1, text: "学习Vue 3", completed: false },
{ id: 2, text: "掌握Composition API", completed: true }
],
filter: "all"
});
// 计算属性
const completedTodos = computed(() => {
return state.todos.filter(todo => todo.completed);
});
const activeTodos = computed(() => {
return state.todos.filter(todo => !todo.completed);
});
// 侦听器
watch(
() => count.value,
(newValue, oldValue) => {
console.log(`count从${oldValue}变为${newValue}`);
}
);
// 深度侦听对象
watch(
() => state.todos,
(newTodos, oldTodos) => {
console.log("todos发生变化", newTodos);
},
{ deep: true }
);
// 方法
const addTodo = (text) => {
state.todos.push({
id: Date.now(),
text,
completed: false
});
};
const toggleTodo = (id) => {
const todo = state.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
};
return {
count,
user,
state,
completedTodos,
activeTodos,
addTodo,
toggleTodo
};
}
};
2.2 组合函数 (Composables)
Composition API的真正威力在于可复用的组合函数:
// 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 doubled = computed(() => count.value * 2);
const squared = computed(() => count.value * count.value);
return {
count,
increment,
decrement,
reset,
doubled,
squared
};
}
// useLocalStorage.js - 本地存储组合函数
import { ref, watch } from "vue";
export function useLocalStorage(key, defaultValue) {
const data = ref(JSON.parse(localStorage.getItem(key)) || defaultValue);
watch(data, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue));
}, { deep: true });
const clear = () => {
localStorage.removeItem(key);
data.value = defaultValue;
};
return {
data,
clear
};
}
// 在组件中使用组合函数
import { useCounter, useLocalStorage } from "./composables";
export default {
setup() {
const counter = useCounter(0, 1);
const todos = useLocalStorage("todos", []);
return {
...counter,
todos: todos.data,
clearTodos: todos.clear
};
}
};
3. 高级模式与最佳实践
3.1 类型安全与TypeScript
import { ref, computed, Ref } from "vue";
interface User {
id: number;
name: string;
email: string;
age: number;
}
interface Todo {
id: number;
text: string;
completed: boolean;
createdAt: Date;
}
// 类型安全的组合函数
export function useUserManagement() {
const users: Ref = ref([]);
const selectedUser: Ref = ref(null);
const addUser = (user: Omit) => {
const newUser: User = {
...user,
id: Date.now()
};
users.value.push(newUser);
};
const removeUser = (id: number) => {
const index = users.value.findIndex(user => user.id === id);
if (index !== -1) {
users.value.splice(index, 1);
}
};
const adultUsers = computed(() => {
return users.value.filter(user => user.age >= 18);
});
return {
users,
selectedUser,
addUser,
removeUser,
adultUsers
};
}
// 在组件中使用
export default defineComponent({
setup() {
const userManager = useUserManagement();
// TypeScript会自动推断类型
userManager.addUser({
name: "Alice",
email: "alice@example.com",
age: 25
});
return {
...userManager
};
}
});
3.2 异步状态管理
import { ref, reactive } from "vue";
export function useAsyncData(fetcher, options = {}) {
const {
immediate = true,
initialData = null,
onSuccess,
onError
} = options;
const state = reactive({
data: initialData,
loading: false,
error: null,
loaded: false
});
const execute = async (...args) => {
state.loading = true;
state.error = null;
try {
const result = await fetcher(...args);
state.data = result;
state.loaded = true;
if (onSuccess) {
onSuccess(result);
}
return result;
} catch (error) {
state.error = error;
if (onError) {
onError(error);
}
throw error;
} finally {
state.loading = false;
}
};
// 立即执行
if (immediate) {
execute();
}
return {
state,
execute,
reload: () => execute()
};
}
// 使用示例
import { useAsyncData } from "./composables/useAsyncData";
export default {
setup() {
const fetchUsers = () =>
fetch("https://api.example.com/users")
.then(res => res.json());
const users = useAsyncData(fetchUsers, {
onSuccess: (data) => {
console.log("用户数据加载成功", data);
},
onError: (error) => {
console.error("加载用户数据失败", error);
}
});
return {
users: users.state,
reloadUsers: users.reload
};
}
};
3.3 性能优化
import { ref, computed, watchEffect, onUnmounted } from "vue";
// 防抖组合函数
export function useDebounce(value, delay = 300) {
const debouncedValue = ref(value.value);
let timeoutId = null;
const updateDebouncedValue = () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
debouncedValue.value = value.value;
}, delay);
};
watchEffect(() => {
updateDebouncedValue();
});
onUnmounted(() => {
clearTimeout(timeoutId);
});
return debouncedValue;
}
// 虚拟滚动组合函数
export function useVirtualScroll({
containerRef,
itemHeight,
totalItems,
buffer = 5
}) {
const scrollTop = ref(0);
const visibleItems = ref([]);
const handleScroll = () => {
if (containerRef.value) {
scrollTop.value = containerRef.value.scrollTop;
}
};
const startIndex = computed(() => {
return Math.max(0, Math.floor(scrollTop.value / itemHeight) - buffer);
});
const endIndex = computed(() => {
const containerHeight = containerRef.value?.clientHeight || 0;
const visibleCount = Math.ceil(containerHeight / itemHeight);
return Math.min(
totalItems - 1,
startIndex.value + visibleCount + buffer * 2
);
});
watchEffect(() => {
const items = [];
for (let i = startIndex.value; i <= endIndex.value; i++) {
items.push({
index: i,
top: i * itemHeight,
data: null // 实际数据需要从数据源获取
});
}
visibleItems.value = items;
});
return {
scrollTop,
visibleItems,
handleScroll,
totalHeight: computed(() => totalItems * itemHeight)
};
}
4. 迁移策略与建议
4.1 渐进式迁移
// 混合模式 - 逐步迁移
import { ref, computed } from "vue";
export default {
// Options API部分
data() {
return {
legacyData: "旧数据"
};
},
computed: {
legacyComputed() {
return this.legacyData.toUpperCase();
}
},
methods: {
legacyMethod() {
console.log("旧方法");
}
},
// Composition API部分
setup() {
const newData = ref("新数据");
const newComputed = computed(() => newData.value + "!");
const newMethod = () => {
console.log("新方法");
};
return {
newData,
newComputed,
newMethod
};
}
};
4.2 工具辅助迁移
// 使用@vue/composition-api插件(Vue 2项目)
import Vue from "vue";
import VueCompositionAPI from "@vue/composition-api";
Vue.use(VueCompositionAPI);
// 在Vue 2组件中使用Composition API
export default {
setup() {
// 可以使用大部分Composition API功能
const count = ref(0);
return {
count
};
}
};
// 使用迁移构建工具
// vue-cli: vue add vue-next
// vite: 原生支持Vue 3
5. 总结与展望
Composition API代表了Vue框架发展的一个重要里程碑。它带来的主要优势包括:
- 更好的代码组织:相关逻辑可以集中在一起,而不是分散在不同的选项中
- 更强的类型支持:与TypeScript的集成更加自然和强大
- 更高的可复用性:组合函数可以轻松地在不同组件间共享
- 更灵活的代码结构:不再受限于Vue的选项结构
- 更好的Tree-shaking:只导入需要的API,减小打包体积
对于新项目,建议直接使用Composition API。对于现有项目,可以采用渐进式迁移策略,逐步将复杂组件的逻辑重构为组合函数。
随着Vue 3生态的成熟,Composition API将成为Vue开发的标准模式。掌握它不仅能让您编写更高质量的Vue代码,还能让您更好地理解现代前端开发的趋势。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END




暂无评论内容