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带来了更灵活、更强大的代码组织方式:
- 更好的逻辑复用:通过自定义composition函数实现逻辑复用
- 更清晰的代码组织:相关逻辑可以组织在一起,而不是分散在各个选项中
- 更好的TypeScript支持:完整的类型推断和类型安全
- 更小的包体积:Tree-shaking友好,只导入需要的API
- 更好的性能:响应式系统重写,性能提升显著
对于新项目,建议直接使用Composition API。对于现有项目,可以逐步迁移,两者可以共存。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END




暂无评论内容