欢迎来到《Vue.js 从新手到专家》的第五章!在这一章中,我们将深入探讨 Vue 3 的核心特性之一:Composition API。与 Options API 不同,Composition API 提供了一种更灵活、更强大的方式来组织和复用组件逻辑。
通过学习本章内容,你将掌握以下技能:
- 理解 Composition API 的基本概念及其优势。
- 使用
ref()
和reactive()
处理数据。 - 掌握生命周期钩子的使用方法。
- 学习如何通过
computed()
创建计算属性。 - 创建可重用的组合式函数(Composable Functions)。
- 完成实践挑战,巩固所学知识。
让我们开始吧!
1 使用 Composition API 设置组件
1.1 Composition API 的基本结构
Composition API 的核心思想是将组件的逻辑按功能拆分,而不是像 Options API 那样按选项组织。这种方式特别适合处理复杂的业务逻辑。
示例:一个简单的计数器组件
<!-- Counter.vue -->
<template>
<div>
<p>当前计数:{{ count }}</p>
<button @click="increment">增加</button>
<button @click="reset">重置</button>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
// 定义响应式数据
const count = ref(0);
// 定义方法
const increment = () => {
count.value++;
console.log("计数增加!");
};
const reset = () => {
count.value = 0;
console.log("计数已重置!");
};
// 返回需要暴露的内容
return {
count,
increment,
reset
};
}
});
</script>
输出结果:
- 页面加载时,显示“当前计数:0”。
- 点击“增加”按钮后,计数递增,并在控制台输出“计数增加!”。
- 点击“重置”按钮后,计数恢复为 0,并在控制台输出“计数已重置!”。
小贴士:
setup()
是 Composition API 的入口函数,所有逻辑都在其中定义。- 在
setup()
中定义的数据和方法需要通过return
暴露给模板。
1.2 Composition API 的优势
- 逻辑复用:通过组合式函数(Composables),可以轻松实现逻辑复用。
- 代码组织:将相关逻辑集中在一起,便于理解和维护。
- TypeScript 支持:提供更好的类型推断和代码补全。
2 使用 ref() 和 reactive() 处理数据
Vue 3 提供了两种创建响应式数据的方式:ref()
和 reactive()
。
2.1 使用 ref()
ref()
用于创建基本类型的响应式数据。
示例:使用 ref()
创建响应式变量
<!-- RefExample.vue -->
<template>
<div>
<p>消息:{{ message }}</p>
<button @click="updateMessage">更新消息</button>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
// 创建响应式变量
const message = ref("初始消息");
// 更新变量值
const updateMessage = () => {
message.value = "消息已更新";
};
return {
message,
updateMessage
};
}
});
</script>
输出结果:
- 页面加载时,显示“消息:初始消息”。
- 点击“更新消息”按钮后,消息变为“消息已更新”。
小贴士:
- 访问或修改
ref
的值时,需要使用.value
属性。ref
适用于基本类型(如字符串、数字、布尔值)。
2.2 使用 reactive()
reactive()
用于创建对象类型的响应式数据。
示例:使用 reactive()
创建响应式对象
<!-- ReactiveExample.vue -->
<template>
<div>
<p>用户名:{{ user.name }}</p>
<p>年龄:{{ user.age }}</p>
<button @click="updateUser">更新用户信息</button>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
setup() {
// 创建响应式对象
const user = reactive({
name: "张三",
age: 25
});
// 更新对象属性
const updateUser = () => {
user.name = "李四";
user.age = 30;
};
return {
user,
updateUser
};
}
});
</script>
输出结果:
- 页面加载时,显示“用户名:张三”和“年龄:25”。
- 点击“更新用户信息”按钮后,用户名变为“李四”,年龄变为“30”。
小贴士:
reactive
返回的是一个代理对象,直接访问或修改其属性即可触发响应式更新。- 对于嵌套对象,
reactive
会自动递归地将其内部属性转换为响应式。
2.3 ref()
和 reactive()
的选择
特性 | ref() | reactive() |
---|---|---|
数据类型 | 基本类型 | 对象类型 |
访问方式 | 需要通过 .value 访问 | 直接访问属性 |
性能 | 轻量级 | 对复杂对象更高效 |
3 使用生命周期钩子
在 Composition API 中,生命周期钩子以函数的形式提供,例如 onMounted
、onUnmounted
等。
3.1 示例:使用生命周期钩子
<!-- LifecycleHooks.vue -->
<template>
<div>
<p>当前计数:{{ count }}</p>
<button @click="increment">增加</button>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, onUnmounted } from "vue";
export default defineComponent({
setup() {
const count = ref(0);
const increment = () => {
count.value++;
};
// 组件挂载时执行
onMounted(() => {
console.log("组件已挂载!");
});
// 组件卸载时执行
onUnmounted(() => {
console.log("组件已卸载!");
});
return {
count,
increment
};
}
});
</script>
输出结果:
- 当组件加载时,控制台输出“组件已挂载!”。
- 当组件卸载时,控制台输出“组件已卸载!”。
小贴士:
- 使用
onMounted
和onUnmounted
可以在组件生命周期的不同阶段执行逻辑。- 如果需要清理资源(如定时器或事件监听器),建议在
onUnmounted
中进行。
4 了解 Composition API 中的观察者
观察者(Watchers)用于监听响应式数据的变化,并在变化时执行回调函数。
4.1 示例:使用 watch()
<!-- WatchExample.vue -->
<template>
<div>
<input v-model="message" placeholder="请输入内容" />
<p>消息长度:{{ messageLength }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, watch, computed } from "vue";
export default defineComponent({
setup() {
const message = ref("");
// 计算属性
const messageLength = computed(() => message.value.length);
// 观察 message 的变化
watch(
message,
(newVal, oldVal) => {
console.log(`消息从 "${oldVal}" 变为 "${newVal}"`);
},
{ immediate: true } // 立即执行一次回调
);
return {
message,
messageLength
};
}
});
</script>
输出结果:
- 用户输入“Hello”时,页面显示“消息长度:5”。
- 控制台输出:
-
消息从 "" 变为 "H" 消息从 "H" 变为 "He" 消息从 "He" 变为 "Hel" 消息从 "Hel" 变为 "Hell" 消息从 "Hell" 变为 "Hello"
小贴士:
watch
的回调函数接收两个参数:新值和旧值。- 使用
{ immediate: true }
可以在初始化时立即执行回调。
4.2 示例:使用 watchEffect()
watchEffect
是一种更简洁的观察方式,它会在依赖发生变化时自动重新执行。
<!-- WatchEffectExample.vue -->
<template>
<div>
<input v-model="message" placeholder="请输入内容" />
</div>
</template>
<script lang="ts">
import { defineComponent, ref, watchEffect } from "vue";
export default defineComponent({
setup() {
const message = ref("");
// 自动观察 message 的变化
watchEffect(() => {
console.log("当前消息:", message.value);
});
return {
message
};
}
});
</script>
输出结果:
每当用户输入内容时,控制台都会输出当前消息。
小贴士:
watchEffect
不需要显式指定依赖,它会自动追踪所有引用的响应式数据。- 如果需要手动控制依赖,建议使用
watch
。
5 使用 computed()
计算属性(Computed Properties)允许基于其他响应式数据动态计算值。
5.1 示例:使用 computed()
<!-- ComputedExample.vue -->
<template>
<div>
<input v-model="firstName" placeholder="请输入名字" />
<input v-model="lastName" placeholder="请输入姓氏" />
<p>完整姓名:{{ fullName }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from "vue";
export default defineComponent({
setup() {
const firstName = ref("");
const lastName = ref("");
// 计算属性
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
return {
firstName,
lastName,
fullName
};
}
});
</script>
输出结果:
假设用户输入名字为“张”,姓氏为“三”,页面将显示:
完整姓名:张 三
小贴士:
- 计算属性是基于依赖自动更新的,因此比手动方法更高效。
- 如果需要双向绑定计算属性,可以使用 getter 和 setter。
6 创建可重用的可组合钩子
组合式函数(Composables)是一组封装了特定逻辑的函数,可以在多个组件中复用。
6.1 示例:创建一个计数器组合式函数
// useCounter.ts
import { ref } from "vue";
export function useCounter() {
const count = ref(0);
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
const reset = () => {
count.value = 0;
};
return { count, increment, decrement, reset };
}
vue
<!-- Counter.vue -->
<template>
<div>
<p>当前计数:{{ count }}</p>
<button @click="increment">增加</button>
<button @click="decrement">减少</button>
<button @click="reset">重置</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { useCounter } from "./useCounter";
export default defineComponent({
setup() {
const { count, increment, decrement, reset } = useCounter();
return {
count,
increment,
decrement,
reset
};
}
});
</script>
输出结果:
- 页面加载时,显示“当前计数:0”。
- 点击“增加”按钮后,计数递增。
- 点击“减少”按钮后,计数递减。
- 点击“重置”按钮后,计数恢复为 0。
小贴士:
- 组合式函数可以封装复杂的逻辑,提升代码的复用性和可维护性。
- 使用 TypeScript 时,建议为组合式函数添加类型声明。
7 实践挑战
为了巩固所学知识,请尝试完成以下挑战:
7.1 挑战:构建一个任务管理应用
要求:
- 创建一个名为
TaskList
的组件,显示任务列表。 - 使用
ref()
或reactive()
管理任务数据。 - 使用
computed()
创建过滤后的任务列表(如仅显示未完成的任务)。 - 使用组合式函数封装任务管理逻辑。
示例代码:
// useTasks.ts
import { ref } from "vue";
export function useTasks() {
const tasks = ref([
{ id: 1, text: "任务 A", completed: false },
{ id: 2, text: "任务 B", completed: true }
]);
const addTask = (text: string) => {
tasks.value.push({ id: Date.now(), text, completed: false });
};
const toggleTask = (id: number) => {
const task = tasks.value.find((t) => t.id === id);
if (task) {
task.completed = !task.completed;
}
};
const deleteTask = (id: number) => {
tasks.value = tasks.value.filter((t) => t.id !== id);
};
return { tasks, addTask, toggleTask, deleteTask };
}
vue
<!-- TaskList.vue -->
<template>
<div>
<h2>任务列表</h2>
<ul>
<li v-for="task in filteredTasks" :key="task.id">
<input type="checkbox" :checked="task.completed" @change="toggleTask(task.id)" />
<span :class="{ completed: task.completed }">{{ task.text }}</span>
<button @click="deleteTask(task.id)">删除</button>
</li>
</ul>
<input v-model="newTaskText" placeholder="请输入新任务" />
<button @click="addTask">添加任务</button>
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from "vue";
import { useTasks } from "./useTasks";
export default defineComponent({
setup() {
const { tasks, addTask, toggleTask, deleteTask } = useTasks();
const newTaskText = ref("");
const filteredTasks = computed(() => tasks.value.filter((t) => !t.completed));
const handleAddTask = () => {
if (newTaskText.value.trim() !== "") {
addTask(newTaskText.value);
newTaskText.value = "";
}
};
return {
tasks,
filteredTasks,
newTaskText,
addTask: handleAddTask,
toggleTask,
deleteTask
};
}
});
</script>
<style scoped>
.completed {
text-decoration: line-through;
color: gray;
}
</style>
输出结果:
- 页面加载时,显示任务列表。
- 添加新任务后,任务被添加到列表中。
- 勾选任务前的复选框,任务文字变为划线效果。
- 点击“删除”按钮后,任务从列表中移除。
8 总结
本章详细介绍了 Vue 3 的 Composition API,包括 ref()
和 reactive()
的使用、生命周期钩子、观察者、计算属性以及组合式函数的创建。通过这些知识点的学习,你已经掌握了如何使用 Composition API 构建高效且可维护的 Vue 应用。
在接下来的章节中,我们将继续探索 Vue.js 的高级特性和最佳实践,帮助你成为一名专业的 Vue.js 开发者!