很多人学了Vue 3,却还在用Vue 2的思维写代码。今天咱们聊聊,为什么组合式API不是"可选项",而是你进阶的"必修课"。
先别急,咱们讲个故事
想象一下,你刚入职一家公司,领导让你整理办公桌。
Options API的整理方式是这样的:所有的笔放一个抽屉,所有的本子放一个抽屉,所有的充电线放一个抽屉。
听起来很整齐对吧?但当你要处理"写周报"这件事时,你需要从笔的抽屉拿笔,从本子的抽屉拿本子,从充电线的抽屉拿充电器给电脑充电——你得满办公桌翻。
Composition API的整理方式则是:把"写周报"需要的东西放一起,把"画设计稿"需要的东西放一起。做什么事,拿什么包,干净利落。
这就是Vue组合式API的核心思想——按功能组织代码,而不是按类型分类。
听起来简单,但这个理念的转变,足以改变你写Vue代码的方式。
Options API到底"哪里不行了"?
咱们先看一个最经典的计数器组件,用Options API写:
<template> <div> <p>Count: {{ count }}</p> <button @click="increment">+1</button> </div> </template> <script> export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } } } </script>看起来没毛病对吧?简单、清晰、教科书般的写法。
但问题来了——当你的组件不只是一个计数器,而是同时要处理用户信息、搜索过滤、分页加载、表单校验四五个功能时,你的代码会变成这样:
+------------------+ | data() | ← count、userInfo、searchKey、pageNum、formData 全堆在一起 +------------------+ | methods | ← increment、fetchUser、handleSearch、loadMore、validate 全堆在一起 +------------------+ | computed | ← doubleCount、fullName、filteredList 全堆在一起 +------------------+ | watch | ← 监听count、监听searchKey、监听pageNum 全堆在一起 +------------------+你发现问题了吗?
"计数器"的逻辑被撕碎了——count在data里,increment在methods里,doubleCount在computed里,watch count在watch里。你要理解"计数器"这一个功能,得在四个地方来回跳。
组件小的时候还好,一旦超过200行,维护起来就像在一碗面条里找一根特定的面——这就是Options API的"面条式代码"问题。
组合式API:把散落的拼图重新拼好
同样的计数器,用Composition API重写:
<template> <div> <p>Count: {{ count }}</p> <button @click="increment">+1</button> </div> </template> <script setup> import { ref } from 'vue' const count = ref(0) function increment() { count.value++ } </script>你会发现几个变化:
第一,没有了this。在Options API里,你访问数据要this.count,调用方法要this.increment()。但在组合式API里,一切都是普通的JavaScript变量和函数。没有this指向的困惑,没有"为什么箭头函数里this不对"的经典面试题。
第二,ref是什么?你可以把ref理解为一个"响应式盒子"。普通的JavaScript变量改了值,Vue不知道;但你把值放进ref这个盒子里,Vue就能自动追踪它的变化。访问或修改盒子里的值,需要用.value。
第三,<script setup>。这是Vue 3推荐的语法糖,它帮你省掉了setup()函数的定义和return语句。在<script setup>里声明的变量和函数,模板可以直接使用。
用一张图来对比两种写法的思维差异:
Options API 的组织方式(按类型分) Composition API 的组织方式(按功能分) ┌─────────────────────┐ ┌─────────────────────┐ │ data: │ │ 🔢 计数器功能: │ │ count │ │ count = ref(0) │ │ searchKey │ │ increment() │ │ userInfo │ │ doubleCount │ ├─────────────────────┤ │ watch(count) │ │ methods: │ ├─────────────────────┤ │ increment │ │ 🔍 搜索功能: │ │ handleSearch │ │ searchKey = ref('')│ │ fetchUser │ │ handleSearch() │ ├─────────────────────┤ │ filteredList │ │ computed: │ ├─────────────────────┤ │ doubleCount │ │ 👤 用户功能: │ │ filteredList │ │ userInfo = ref({}) │ │ fullName │ │ fetchUser() │ ├─────────────────────┤ │ fullName │ │ watch: │ └─────────────────────┘ │ count │ │ searchKey │ └─────────────────────┘左边是"按抽屉分类",右边是"按事情分类"。哪个更容易维护,一目了然。
进阶三板斧:computed、watch、生命周期
掌握了ref,咱们再学三个核心工具。
第一板斧:computed——自动计算的"公式单元格"
用过Excel吗?你在A1输入数字,B1写个公式=A1*2,A1一变,B1自动变。computed就是Vue里的"公式单元格"。
<script setup> import { ref, computed } from 'vue' const count = ref(0) function increment() { count.value++ } // doubleCount 会随 count 自动更新,不需要你手动维护 const doubleCount = computed(() => count.value * 2) </script>doubleCount不是一个普通变量,它是一个响应式的计算属性。count变了,doubleCount自动变。你不需要写任何"同步逻辑",Vue帮你搞定。
第二板斧:watch——变化的"监控摄像头"
有时候你不只是想"自动算",还想在数据变化时做点什么事——比如发请求、记日志、弹提示。这时候就需要watch。
<script setup> import { ref, watch } from 'vue' const count = ref(0) function increment() { count.value++ } watch(count, (newVal, oldVal) => { console.log(`计数从 ${oldVal} 变成了 ${newVal}`) // 实际场景:比如搜索框输入变化时,自动请求接口 // 比如分页页码变化时,自动加载下一页数据 }) </script>watch的回调会告诉你新值和旧值,让你能精确地响应变化。在实际项目中,watch最常见的用途包括:
监听搜索关键词变化,做防抖请求
监听路由参数变化,重新加载页面数据
监听表单字段变化,实时校验
第三板斧:onMounted——组件"上岗"后的第一件事
组件挂载到页面上之后,你通常需要做一些初始化工作——请求接口、初始化第三方库、设置定时器。这就是onMounted的用武之地。
<script setup> import { ref, onMounted } from 'vue' const message = ref('加载中...') onMounted(() => { // 模拟接口请求 setTimeout(() => { message.value = '数据加载完成 ✅' }, 1000) }) </script> <template> <p>{{ message }}</p> </template>在Options API里,你得把这段逻辑写在mounted()选项里。但在组合式API中,onMounted就是一个普通函数调用,你可以把它和相关的数据、方法写在一起,不再需要"跳来跳去"。
组合式API的"杀手级特性":Composables
前面讲的都是"怎么在一个组件里用"。但组合式API真正的威力,在于跨组件复用逻辑。
在Options API时代,复用逻辑主要靠mixins。但用过的人都知道,mixins有三大坑:
Mixins 的三大坑: ┌──────────────────────────────────────────────┐ │ ❌ 命名冲突:多个mixin可能定义同名属性/方法 │ │ ❌ 来源不明:模板里用的变量,不知道来自哪个mixin │ │ ❌ 隐式依赖:mixin之间可能存在看不见的依赖关系 │ └──────────────────────────────────────────────┘组合式API的解决方案是Composables(组合式函数)。它本质上就是一个普通的JavaScript函数,返回响应式数据和方法。
来看个实际例子。假设你的项目里有多个组件都需要"计数器"功能:
// useCounter.js import { ref } from'vue' exportfunction useCounter(initialValue = 0) { const count = ref(initialValue) function increment() { count.value++ } function decrement() { count.value-- } function reset() { count.value = initialValue } return { count, increment, decrement, reset } }在任何组件里使用:
<script setup> import { useCounter } from './useCounter' // 用法一:默认从0开始 const { count, increment, decrement, reset } = useCounter() // 用法二:从100开始计数 const { count: score, increment: addScore } = useCounter(100) </script>看到了吗?这就是普通的JavaScript函数调用和解构赋值。没有黑魔法,没有隐式注入,每个变量从哪来,一目了然。
再来一个更贴近实际开发的例子——封装一个通用的"接口请求"组合函数:
// useFetch.js import { ref } from'vue' export function useFetch(url) { const data = ref(null) const loading = ref(true) const error = ref(null) fetch(url) .then(res => res.json()) .then(json => { data.value = json }) .catch(err => { error.value = err.message }) .finally(() => { loading.value = false }) return { data, loading, error } }在组件中使用:
<script setup> import { useFetch } from './useFetch' const { data, loading, error } = useFetch('https://api.example.com/users') </script> <template> <div v-if="loading">加载中...</div> <div v-else-if="error">出错了:{{ error }}</div> <ul v-else> <li v-for="user in data" :key="user.id">{{ user.name }}</li> </ul> </template>三行代码搞定接口请求、加载状态、错误处理。这种复用方式,比mixins不知道高到哪里去了。
一张图看懂:什么时候用Composition API?
你的场景是什么? │ ▼ ┌───────────────┐ 是 ┌──────────────────────┐ │ 简单的小组件? ├────────→│ Options API 完全够用 │ │(< 100行代码)│ │ 不用强行切换 │ └───────┬───────┘ └──────────────────────┘ │ 否 ▼ ┌───────────────┐ 是 ┌──────────────────────┐ │ 需要跨组件 ├────────→│ Composables 是最优解 │ │ 复用逻辑? │ │ 告别 mixins │ └───────┬───────┘ └──────────────────────┘ │ 否 ▼ ┌───────────────┐ 是 ┌──────────────────────┐ │ 组件逻辑复杂? ├────────→│ Composition API │ │(多个关注点) │ │ 按功能分组,更好维护 │ └───────┬───────┘ └──────────────────────┘ │ 否 ▼ ┌──────────────────────────┐ │ 新项目 / 团队协作? │ │ → 推荐 Composition API │ │ 统一代码风格,降低协作成本│ └──────────────────────────┘写在最后:不是"新的"替代"旧的",而是思维的升级
很多同学一看到"新API"就焦虑:是不是Options API要被废弃了?我之前学的是不是白学了?
大可不必。Vue官方明确表示:Options API不会被移除,两种写法会长期共存。
但如果你问我的建议——
如果你是Vue新手,直接从Composition API学起。它更接近原生JavaScript,理解成本反而更低。
如果你是Vue老手,新功能用Composition API写,老代码按需迁移。不必一刀切,但掌握这个能力是进阶的必经之路。
如果你是团队Leader,新项目统一Composition API +<script setup>。代码一致性和可维护性的提升,你的组员会感谢你的。
组合式API的本质不是语法层面的更新,而是代码组织思维的升级。它让你从"按类型分类"转向"按功能分类",让代码像搭积木一样灵活组合。
这一步,迟早要迈。不如,就从今天开始。
如果这篇文章对你有帮助,欢迎点赞 👍让更多人看到,分享 🔗给你身边正在学Vue的朋友,也欢迎在评论区聊聊你从Options API切换到Composition API的体验!
关注「前端达人」,和数万前端开发者一起进阶,每周分享最实用的前端干货 🚀