当前位置: 首页 > news >正文

从零基础到最佳实践:Vue.js 系列(6/10):《Composition API(组合式 API)》

引言

Vue.js 作为前端开发领域的热门框架之一,在 Vue 3 中引入了 Composition API(组合式 API),为开发者带来了全新的代码组织方式。相比传统的 Options API,Composition API 更加灵活,能够更好地应对复杂逻辑的开发需求。它通过将相关逻辑集中在一起,提升了代码的可读性和可维护性,成为现代 Vue 开发的标配。

本文将带你从零开始学习 Composition API,涵盖基础用法、进阶技巧以及大量实际开发场景。无论你是刚接触 Vue 的新手,还是希望深入掌握 Vue 3 的老手,这篇文章都能为你提供实用的知识和灵感。


一、Composition API 基础

1.1 什么是 setup 函数?

setup 函数是 Composition API 的核心,它是组件的入口点,在组件创建时被调用,用于定义响应式数据、计算属性、方法等。它接收两个参数:

  • props:组件接收的外部属性。
  • context:包含 attrs(非 prop 属性)、slots(插槽)和 emit(事件触发器)等。

简单示例

<template><div>{{ count }} * 2 = {{ double }}</div><button @click="increment">加 1</button>
</template><script>
import { ref, computed } from 'vue';export default {setup() {const count = ref(0); // 响应式数据const double = computed(() => count.value * 2); // 计算属性const increment = () => { count.value++; }; // 方法return { count, double, increment }; // 返回给模板使用},
};
</script>

在这个例子中,setup 函数定义了 count(计数器)、double(计算属性)和 increment(加 1 方法),并通过 return 将它们暴露给模板。

1.2 响应式数据:refreactive

Vue 的响应式系统是其核心特性之一,Composition API 提供了 refreactive 来创建响应式数据。

  • ref:用于基本数据类型(如数字、字符串)的响应式引用。访问和修改时需要使用 .value
  • reactive:用于复杂数据类型(如对象、数组)的响应式引用,不需要 .value

示例

<template><div>{{ count }} - {{ user.name }}</div><button @click="count++">加 1</button><button @click="user.name = '李四'">改名</button>
</template><script>
import { ref, reactive } from 'vue';export default {setup() {const count = ref(0);const user = reactive({ name: '张三', age: 18 });return { count, user };},
};
</script>

注意:在模板中,ref 的值会自动解包,无需写 .value

1.3 计算属性:computed

computed 用于创建依赖于其他响应式数据的计算属性,只有当依赖发生变化时才会重新计算。

示例

<template><div>总价:{{ totalPrice }}</div>
</template><script>
import { ref, computed } from 'vue';export default {setup() {const price = ref(100);const quantity = ref(2);const totalPrice = computed(() => price.value * quantity.value);return { totalPrice };},
};
</script>

1.4 侦听器:watchwatchEffect

  • watch:显式监听某个数据源的变化,并执行回调函数。
  • watchEffect:自动追踪依赖的变化并执行副作用。

示例

<template><div>{{ count }}</div><button @click="count++">加 1</button>
</template><script>
import { ref, watch, watchEffect } from 'vue';export default {setup() {const count = ref(0);// watch:显式监听 countwatch(count, (newVal, oldVal) => {console.log(`count 从 ${oldVal} 变为 ${newVal}`);});// watchEffect:自动追踪依赖watchEffect(() => {console.log(`count 当前值:${count.value}`);});return { count };},
};
</script>

二、进阶用法

2.1 生命周期钩子

Composition API 提供了与 Options API 类似的生命周期钩子,如 onMountedonUpdated 等,在 setup 中使用。

示例

<template><div>{{ message }}</div>
</template><script>
import { ref, onMounted, onUnmounted } from 'vue';export default {setup() {const message = ref('Hello');onMounted(() => {console.log('组件已挂载');});onUnmounted(() => {console.log('组件已卸载');});return { message };},
};
</script>

2.2 依赖注入:provideinject

provideinject 用于跨组件传递数据,特别适合祖孙组件通信。

示例

<!-- 父组件 -->
<template><Child />
</template><script>
import { provide } from 'vue';
import Child from './Child.vue';export default {setup() {provide('config', { theme: 'dark' });},components: { Child },
};
</script><!-- 子组件 -->
<template><div>主题:{{ theme }}</div>
</template><script>
import { inject } from 'vue';export default {setup() {const config = inject('config');return { theme: config.theme };},
};
</script>

2.3 自定义 Hooks

自定义 Hooks 是 Composition API 的亮点之一,可以封装可复用的逻辑。

示例:鼠标位置追踪 Hook

// useMouse.js
import { ref, onMounted, onUnmounted } from 'vue';export function useMouse() {const x = ref(0);const y = ref(0);const update = (event) => {x.value = event.pageX;y.value = event.pageY;};onMounted(() => {window.addEventListener('mousemove', update);});onUnmounted(() => {window.removeEventListener('mousemove', update);});return { x, y };
}

使用

<template><div>鼠标位置:{{ x }}, {{ y }}</div>
</template><script>
import { useMouse } from './useMouse';export default {setup() {const { x, y } = useMouse();return { x, y };},
};
</script>

三、实际开发应用场景

3.1 表单处理与验证

案例:用户注册表单

<template><form @submit.prevent="submit"><input v-model="form.username" placeholder="用户名" /><input v-model="form.password" type="password" placeholder="密码" /><div v-if="errors.username">{{ errors.username }}</div><div v-if="errors.password">{{ errors.password }}</div><button type="submit" :disabled="hasErrors">提交</button></form>
</template><script>
import { reactive, computed } from 'vue';export default {setup() {const form = reactive({username: '',password: '',});const errors = computed(() => {const errs = {};if (!form.username) errs.username = '用户名不能为空';if (form.password.length < 6) errs.password = '密码至少 6 位';return errs;});const hasErrors = computed(() => Object.keys(errors.value).length > 0);const submit = () => {if (!hasErrors.value) {console.log('提交成功:', form);}};return { form, errors, hasErrors, submit };},
};
</script>

3.2 数据请求与状态管理

案例:获取用户列表

<template><div v-if="loading">加载中...</div><div v-else-if="error">{{ error }}</div><ul v-else><li v-for="user in users" :key="user.id">{{ user.name }}</li></ul>
</template><script>
import { ref, onMounted } from 'vue';export default {setup() {const users = ref([]);const loading = ref(true);const error = ref(null);const fetchUsers = async () => {try {const res = await fetch('https://jsonplaceholder.typicode.com/users');users.value = await res.json();} catch (err) {error.value = '加载失败:' + err.message;} finally {loading.value = false;}};onMounted(fetchUsers);return { users, loading, error };},
};
</script>

3.3 组件通信与状态共享

案例:主题切换

<!-- App.vue -->
<template><div :class="theme"><ThemeToggle /><Child /></div>
</template><script>
import { ref, provide } from 'vue';
import ThemeToggle from './ThemeToggle.vue';
import Child from './Child.vue';export default {setup() {const theme = ref('light');provide('theme', theme);return { theme };},components: { ThemeToggle, Child },
};
</script><!-- ThemeToggle.vue -->
<template><button @click="toggleTheme">切换主题</button>
</template><script>
import { inject } from 'vue';export default {setup() {const theme = inject('theme');const toggleTheme = () => {theme.value = theme.value === 'light' ? 'dark' : 'light';};return { toggleTheme };},
};
</script><!-- Child.vue -->
<template><div>当前主题:{{ theme }}</div>
</template><script>
import { inject } from 'vue';export default {setup() {const theme = inject('theme');return { theme };},
};
</script>

3.4 动态表格与分页

案例:带分页的用户表格

<template><div><table><thead><tr><th>ID</th><th>姓名</th><th>邮箱</th></tr></thead><tbody><tr v-for="user in paginatedUsers" :key="user.id"><td>{{ user.id }}</td><td>{{ user.name }}</td><td>{{ user.email }}</td></tr></tbody></table><button @click="prevPage" :disabled="currentPage === 1">上一页</button><span>第 {{ currentPage }} 页</span><button @click="nextPage" :disabled="currentPage === totalPages">下一页</button></div>
</template><script>
import { ref, computed, onMounted } from 'vue';export default {setup() {const users = ref([]);const currentPage = ref(1);const pageSize = 5;const totalPages = computed(() => Math.ceil(users.value.length / pageSize));const paginatedUsers = computed(() => {const start = (currentPage.value - 1) * pageSize;const end = start + pageSize;return users.value.slice(start, end);});const fetchUsers = async () => {const res = await fetch('https://jsonplaceholder.typicode.com/users');users.value = await res.json();};const prevPage = () => { if (currentPage.value > 1) currentPage.value--; };const nextPage = () => { if (currentPage.value < totalPages.value) currentPage.value++; };onMounted(fetchUsers);return { paginatedUsers, currentPage, totalPages, prevPage, nextPage };},
};
</script>

四、优化技巧

4.1 性能优化

  • 减少不必要计算:使用 computed 缓存计算结果,避免重复计算。
  • 控制 watchEffect:尽量减少依赖项,防止频繁触发。
  • 解构响应式对象:使用 toRefs 保持响应性。

示例

import { reactive, toRefs } from 'vue';const state = reactive({ count: 0, name: 'Vue' });
const { count, name } = toRefs(state); // 解构后仍保持响应性

4.2 代码组织与重用

  • 抽离逻辑:将复杂逻辑封装为自定义 Hooks。
  • 模块化:将功能按模块拆分到单独文件中。

示例:计数器 Hook

// useCounter.js
import { ref } from 'vue';export function useCounter(initialValue = 0) {const count = ref(initialValue);const increment = () => { count.value++; };const decrement = () => { count.value--; };return { count, increment, decrement };
}

使用

<template><div>{{ count }}</div><button @click="increment">加 1</button><button @click="decrement">减 1</button>
</template><script>
import { useCounter } from './useCounter';export default {setup() {const { count, increment, decrement } = useCounter(10);return { count, increment, decrement };},
};
</script>

4.3 错误处理

在异步操作中添加完善的错误处理逻辑。

示例

<template><div v-if="error">{{ error }}</div><div v-else>{{ data }}</div>
</template><script>
import { ref, onMounted } from 'vue';export default {setup() {const data = ref(null);const error = ref(null);const fetchData = async () => {try {const res = await fetch('https://api.example.com/data');if (!res.ok) throw new Error('网络错误');data.value = await res.json();} catch (err) {error.value = err.message;}};onMounted(fetchData);return { data, error };},
};
</script>

五、未来趋势展望

随着 Vue 3 的普及,Composition API 将成为 Vue 开发的主流方式。它与 TypeScript 的深度结合将为大型项目提供更好的类型支持。此外,社区可能会推出更多基于 Composition API 的插件和工具,如状态管理库、表单处理库等,进一步丰富 Vue 生态。

未来,Composition API 还可能与 Vite 等现代构建工具深度整合,提升开发体验和性能。


六、总结

Composition API 是 Vue 3 的核心特性之一,它不仅提升了代码的灵活性,还为开发者提供了强大的工具来应对复杂场景。通过本文的学习,你应该能够掌握从基础到进阶的用法,并在实际开发中灵活运用。

希望你能在实践中不断探索 Composition API 的潜力,构建出高效、可维护的前端应用!

这篇文章详细介绍了 Composition API 的方方面面,包含多个实用案例和优化建议,适合不同层次的开发者学习和参考。希望对你有所帮助!

相关文章:

  • Redis集群在NoSQL中的应用与优化策略
  • 如何用数据可视化提升你的决策力?
  • CNN vs ViT:图像世界的范式演进
  • 英伟达CEO黄仁勋COMPUTEX 2025演讲实录:AI工厂时代已来,Blackwell架构全面投产
  • Java之函数式接口、lambda表达式、stream流操作、Optional容器、方法引用
  • 基于两阶段交互控制框架的互联多能系统协同自治优化
  • 架构图 C4 规范简介
  • vscode打开的文件被覆盖/只能打开两个文件(Visual Studio Code)
  • ollama使用gpu运行大模型
  • 【Java学习方法】类变量
  • STM32单片机GUI系统1 GUI基本内容
  • 六台升降台完整的限位保护逻辑
  • java 在用redis 的时候,如何合理的处理分页问题? redis应当如何存储性能最佳
  • 【聚合MQ管理 第一章】一个项目管理多种MQ 之 ActiveMq
  • Manus与DeepSeek 的区别
  • Swagger在java的运用
  • Java基础 Day17
  • 云渲染技术解析与渲酷平台深度测评:如何实现高效3D创作?
  • OpenCV CUDA模块特征检测与描述------用于创建一个最大值盒式滤波器(Max Box Filter)函数createBoxMaxFilter()
  • Redis全攻略:解锁高性能数据存储与处理的奥秘
  • 青岛 外语网站建设/seo优化官网
  • 深圳网站建设首选/东莞seo项目优化方法
  • 网站规划与开发技术/同城推广有什么平台
  • wordpress文学模版/seo优化服务商