文章目录
一、引言:为什么需要 Composition API?
在 Vue 2 的 Options API 中,开发者通过 data
、methods
、computed
等选项组织代码。虽然逻辑清晰,但随着组件复杂度增加,跨选项的逻辑复用变得困难(例如,一个功能可能同时需要修改 data
、调用 methods
和监听生命周期)。Vue 3 引入的 Composition API 通过函数式编程解决了这一问题,允许将相关逻辑聚合在一起,提升代码的可维护性和复用性。
本文将深入解析 Composition API 的四大核心工具:ref
、computed
、onMounted
和 onBeforeMount
,并通过代码示例展示它们的实际应用场景。
二、ref
:响应式状态的基石
2.1 什么是 ref
?
ref
是 Vue 3 中创建响应式引用的核心 API。它可以包装任何类型的值(基本类型或对象),并返回一个带有 .value
属性的响应式对象。在模板中,Vue 会自动解包 .value
,开发者无需手动访问。
2.2 基本用法
import { ref } from 'vue';
const count = ref(0); // 包装数字
const message = ref('Hello Vue 3'); // 包装字符串
const user = ref({ name: 'Alice' }); // 包装对象
// 访问值需通过 .value
console.log(count.value); // 0
count.value++; // 修改值
2.3 模板中的自动解包
在模板中直接使用 ref
变量,无需 .value
:
<template>
<div>
<p>{{ count }}</p> <!-- 自动解包为 0 -->
<button @click="count++">Increment</button>
</div>
</template>
2.4 为什么需要 ref
?
- 响应式更新:当
.value
变化时,依赖它的组件会自动重新渲染。 - 类型安全:与 Vue 2 的
this.$data
不同,ref
在 TypeScript 中能提供更好的类型推断。 - 灵活性:可以包装任何值,包括对象、数组甚至函数。
2.5 注意事项
- 对象引用:直接替换整个对象时需重新赋值(如
user.value = { name: 'Bob' }
),而非修改内部属性(如user.value.name = 'Bob'
也能触发响应,但推荐前者以保持一致性)。 - 避免滥用:对于简单组件,Options API 可能更简洁;
ref
更适合复杂逻辑或需要复用的场景。
三、computed
:派生状态的优化器
3.1 什么是 computed
?
computed
用于创建计算属性,基于其他响应式数据派生新值。它会自动缓存结果,只有依赖变化时才重新计算,避免不必要的开销。
3.2 基本用法
import { ref, computed } from 'vue';
const count = ref(0);
const doubleCount = computed(() => count.value * 2); // 依赖 count
console.log(doubleCount.value); // 0
count.value = 3;
console.log(doubleCount.value); // 6(自动更新)
3.3 可写计算属性
默认情况下,computed
是只读的。若需可写,可以传入一个带有 get
和 set
的对象:
const fullName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (newValue) => {
const [first, last] = newValue.split(' ');
firstName.value = first;
lastName.value = last;
}
});
3.4 与 ref
+ 方法对比
- 方法:每次调用都会重新计算,无缓存。
const getDoubleCount = () => count.value * 2; // 每次调用都计算
- 计算属性:仅依赖变化时重新计算,适合频繁使用的派生值。
3.5 适用场景
- 复杂计算(如过滤列表、格式化数据)。
- 需要缓存的派生状态(如表单验证结果)。
- 与
v-model
结合实现双向绑定。
四、生命周期钩子:掌控组件时序
4.1 为什么需要生命周期钩子?
组件从创建到销毁会经历多个阶段(如初始化、挂载、更新、卸载)。生命周期钩子允许开发者在特定时机执行逻辑(如 API 调用、DOM 操作)。
4.2 onBeforeMount
:挂载前的最后准备
- 触发时机:在组件完成模板编译,但尚未渲染到 DOM 之前调用。
- 典型用途:
- 读取最终 DOM 尺寸(需等待渲染,通常用
onMounted
更合适)。 - 初始化非响应式第三方库(如图表库)。
- 读取最终 DOM 尺寸(需等待渲染,通常用
import { onBeforeMount } from 'vue';
onBeforeMount(() => {
console.log('组件即将挂载,DOM 未就绪');
});
4.3 onMounted
:DOM 就绪的信号
- 触发时机:组件挂载到 DOM 后立即调用。
- 典型用途:
- 执行 API 请求。
- 操作 DOM(如聚焦输入框)。
- 启动定时器或事件监听。
import { onMounted, ref } from 'vue';
const data = ref(null);
onMounted(async () => {
const response = await fetch('/api/data');
data.value = await response.json();
});
4.4 其他常用钩子
onBeforeUpdate
/onUpdated
:响应式数据变化时触发。onUnmounted
:组件卸载时清理资源(如取消订阅、清除定时器)。
4.5 对比 Options API
Composition API | Options API |
---|---|
onMounted(() => {}) |
mounted() {} |
onBeforeMount(() => {}) |
beforeMount() {} |
Composition API 的钩子通过函数调用注册,更灵活且易于提取逻辑。
五、实战案例:组合使用 ref
、computed
和生命周期钩子
5.1 场景:用户列表展示与搜索
import { ref, computed, onMounted } from 'vue';
export default {
setup() {
// 响应式状态
const users = ref([]);
const searchQuery = ref('');
const isLoading = ref(false);
// 计算属性:过滤用户
const filteredUsers = computed(() => {
return users.value.filter(user =>
user.name.toLowerCase().includes(searchQuery.value.toLowerCase())
);
});
// 生命周期钩子:加载数据
onMounted(async () => {
isLoading.value = true;
try {
const response = await fetch('https://api.example.com/users');
users.value = await response.json();
} finally {
isLoading.value = false;
}
});
// 返回模板可用的变量和方法
return {
users: filteredUsers, // 暴露计算属性而非原始数据
searchQuery,
isLoading
};
}
};
5.2 模板部分
<template>
<div>
<input v-model="searchQuery" placeholder="搜索用户..." />
<div v-if="isLoading">加载中...</div>
<ul v-else>
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</template>
5.3 代码解析
ref
:管理users
、searchQuery
和isLoading
的状态。computed
:根据searchQuery
实时过滤用户列表。onMounted
:组件挂载后自动加载数据,并处理加载状态。
6、总结与展望
6.1 核心工具回顾
ref
:创建响应式状态。computed
:缓存派生状态。onMounted
/onBeforeMount
:控制组件生命周期。
6.2 Composition API 的优势
- 逻辑复用:通过自定义组合式函数(Composables)提取公共逻辑。
- 更好的 TypeScript 支持:明确的类型推断和接口定义。
- 更灵活的代码组织:按功能而非选项划分代码。
6.3 进一步学习建议
- 探索
watch
和watchEffect
:处理异步依赖和副作用。 - 学习
provide
/inject
:实现跨组件通信。 - 尝试 Pinia:基于 Composition API 的状态管理库。
Vue 3 的 Composition API 为前端开发带来了更现代化的编程范式。通过掌握 ref
、computed
和生命周期钩子,你可以更高效地构建复杂应用,同时保持代码的清晰和可维护性。