Vue 3.5 Composition API最佳实践:从入门到精通
Vue 3.5带来了更强大的Composition API,彻底改变了我们组织Vue组件逻辑的方式。本文将深入探讨Composition API的最佳实践,帮助你写出更清晰、更可维护的Vue代码。
1. 为什么选择Composition API?
在Options API中,相关逻辑被分散到不同的选项中(data、methods、computed等)。Composition API允许我们按功能组织代码,让相关逻辑保持在一起。
// Options API - 逻辑分散
{
data() {
return {
count: 0,
searchQuery: "",
filteredItems: []
};
},
methods: {
increment() {
this.count++;
},
filterItems() {
// 过滤逻辑
}
},
computed: {
doubleCount() {
return this.count * 2;
}
},
watch: {
searchQuery() {
this.filterItems();
}
}
}
// Composition API - 按功能组织
import { ref, computed, watch } from "vue";
// 计数器功能
function useCounter() {
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
function increment() {
count.value++;
}
return { count, doubleCount, increment };
}
// 搜索功能
function useSearch(items) {
const searchQuery = ref("");
const filteredItems = computed(() => {
return items.filter(item =>
item.name.includes(searchQuery.value)
);
});
return { searchQuery, filteredItems };
}
2. 自定义Composable:代码复用的艺术
Composable是Composition API的核心概念,让我们可以创建可复用的逻辑单元。
// useMouse.js - 鼠标位置跟踪
import { ref, onMounted, onUnmounted } from "vue";
export function useMouse() {
const x = ref(0);
const y = ref(0);
function update(event) {
x.value = event.pageX;
y.value = event.pageY;
}
onMounted(() => window.addEventListener("mousemove", update));
onUnmounted(() => window.removeEventListener("mousemove", update));
return { x, y };
}
// useFetch.js - 数据获取
import { ref } from "vue";
export function useFetch(url) {
const data = ref(null);
const error = ref(null);
const loading = ref(false);
async function fetchData() {
loading.value = true;
try {
const response = await fetch(url);
data.value = await response.json();
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
}
// 自动执行
fetchData();
return { data, error, loading, refetch: fetchData };
}
// useLocalStorage.js - 本地存储
import { ref, watch } from "vue";
export function useLocalStorage(key, defaultValue) {
const data = ref(JSON.parse(
localStorage.getItem(key) || JSON.stringify(defaultValue)
));
watch(data, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue));
}, { deep: true });
return data;
}
3. 响应式进阶技巧
Vue 3.5提供了更强大的响应式工具。
import { ref, reactive, computed, watch, watchEffect, toRefs } from "vue";
// 1. ref vs reactive
// ref: 用于基本类型,通过.value访问
const count = ref(0);
// reactive: 用于对象,自动深度响应式
const user = reactive({
name: "张三",
age: 30,
address: {
city: "北京",
street: "朝阳路"
}
});
// 2. computed的妙用
const fullName = computed(() => `${user.name} (${user.age}岁)`);
// 带setter的computed
const editableFullName = computed({
get() {
return `${user.name} ${user.age}`;
},
set(newValue) {
const [name, age] = newValue.split(" ");
user.name = name;
user.age = parseInt(age);
}
});
// 3. watch的高级用法
// 深度监听对象
watch(
() => user.address,
(newAddress, oldAddress) => {
console.log("地址变化:", newAddress);
},
{ deep: true }
);
// 监听多个源
watch(
[() => user.name, () => user.age],
([newName, newAge], [oldName, oldAge]) => {
console.log(`姓名从${oldName}变为${newName}`);
console.log(`年龄从${oldAge}变为${newAge}`);
}
);
// 4. watchEffect - 自动依赖收集
watchEffect(() => {
console.log(`用户信息更新: ${user.name}, ${user.age}`);
// 自动追踪user.name和user.age的依赖
});
// 5. toRefs - 解构响应式对象
const { name, age } = toRefs(user);
// 现在name和age仍然是响应式的ref
4. 组件通信新模式
Composition API提供了新的组件通信方式。
// 父组件
import { provide } from "vue";
import ChildComponent from "./ChildComponent.vue";
export default {
components: { ChildComponent },
setup() {
const theme = reactive({
primary: "#1890ff",
secondary: "#52c41a"
});
// 提供数据给所有子组件
provide("theme", theme);
return { theme };
}
};
// 子组件
import { inject } from "vue";
export default {
setup() {
// 注入父组件提供的数据
const theme = inject("theme");
return { theme };
}
};
// 带默认值的注入
const theme = inject("theme", {
primary: "#000000",
secondary: "#ffffff"
});
// 工厂函数注入
const user = inject("user", () => {
// 仅在需要时创建默认值
return { name: "Guest", role: "viewer" };
});
5. TypeScript集成最佳实践
Vue 3.5与TypeScript的集成更加完美。
// 类型安全的Composable
import { ref, Ref } from "vue";
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);
async function fetchUser(id: number) {
loading.value = true;
try {
const response = await fetch(`/api/users/${id}`);
user.value = await response.json();
} finally {
loading.value = false;
}
}
return { user, loading, fetchUser };
}
// 组件Props类型定义
import { defineComponent, PropType } from "vue";
interface Product {
id: number;
name: string;
price: number;
category: string;
}
export default defineComponent({
props: {
// 基本类型
title: {
type: String,
required: true
},
// 复杂类型
product: {
type: Object as PropType,
required: true
},
// 数组类型
tags: {
type: Array as PropType,
default: () => []
},
// 联合类型
status: {
type: String as PropType<"active" | "inactive" | "pending">,
default: "active"
}
},
setup(props) {
// props有完整的类型提示
console.log(props.product.name);
return {};
}
});
6. 性能优化技巧
// 1. 使用shallowRef/shallowReactive减少不必要的深度响应式
import { shallowRef, shallowReactive } from "vue";
// 大型对象,内部变化不需要响应式
const largeConfig = shallowReactive({
// 数千个配置项
});
// 2. 使用markRaw跳过响应式转换
import { markRaw } from "vue";
const heavyLibrary = markRaw({
// 第三方库实例,不需要响应式
heavyMethod() { /* ... */ }
});
// 3. 合理使用computed缓存
const expensiveCalculation = computed(() => {
// 复杂计算,结果会被缓存
return performHeavyCalculation(data.value);
});
// 4. 使用watch的flush选项控制触发时机
watch(
source,
() => {
// DOM更新后执行
},
{ flush: "post" }
);
// 5. 使用effectScope管理副作用
import { effectScope } from "vue";
const scope = effectScope();
scope.run(() => {
// 在这个作用域内的所有响应式效果
const doubled = computed(() => count.value * 2);
watch(count, () => console.log("count changed"));
});
// 一次性清理所有副作用
scope.stop();
总结
Vue 3.5的Composition API为我们提供了更灵活、更强大的工具来组织Vue应用逻辑。通过自定义Composable、合理的响应式使用、类型安全的TypeScript集成以及性能优化技巧,我们可以构建出更健壮、更易维护的Vue应用。
关键要点:
- 按功能组织代码,而不是按选项
- 创建可复用的Composable函数
- 理解ref和reactive的适用场景
- 充分利用TypeScript的类型系统
- 注意性能优化,避免不必要的响应式
随着Vue生态的不断发展,Composition API将成为Vue开发的标准模式。掌握这些最佳实践,你将在Vue开发中游刃有余。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END




暂无评论内容