
很多人学了Vue 3,却还在用Vue 2的思维写代码。今天咱们聊聊,为什么组合式API不是"可选项",而是你进阶的"必修课"。
先别急,咱们讲个故事
想象一下,你刚入职一家公司,领导让你整理办公桌。
Options API的整理方式是这样的:所有的笔放一个抽屉,所有的本子放一个抽屉,所有的充电线放一个抽屉。
听起来很整齐对吧?但当你要处理"写周报"这件事时,你需要从笔的抽屉拿笔,从本子的抽屉拿本子,从充电线的抽屉拿充电器给电脑充电------你得满办公桌翻。
Composition API的整理方式则是:把"写周报"需要的东西放一起,把"画设计稿"需要的东西放一起。做什么事,拿什么包,干净利落。
这就是Vue组合式API的核心思想------按功能组织代码,而不是按类型分类。
听起来简单,但这个理念的转变,足以改变你写Vue代码的方式。
Options API到底"哪里不行了"?
咱们先看一个最经典的计数器组件,用Options API写:
go
<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>
看起来没毛病对吧?简单、清晰、教科书般的写法。
但问题来了------当你的组件不只是一个计数器,而是同时要处理用户信息、搜索过滤、分页加载、表单校验四五个功能时,你的代码会变成这样:
go
+------------------+
| 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重写:
go
<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>里声明的变量和函数,模板可以直接使用。
用一张图来对比两种写法的思维差异:
go
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里的"公式单元格"。
go
<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。
go
<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的用武之地。
go
<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有三大坑:
go
Mixins 的三大坑:
┌──────────────────────────────────────────────┐
│ ❌ 命名冲突:多个mixin可能定义同名属性/方法 │
│ ❌ 来源不明:模板里用的变量,不知道来自哪个mixin │
│ ❌ 隐式依赖:mixin之间可能存在看不见的依赖关系 │
└──────────────────────────────────────────────┘
组合式API的解决方案是Composables(组合式函数)。它本质上就是一个普通的JavaScript函数,返回响应式数据和方法。
来看个实际例子。假设你的项目里有多个组件都需要"计数器"功能:
go
// 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 }
}
在任何组件里使用:
go
<script setup>
import { useCounter } from './useCounter'
// 用法一:默认从0开始
const { count, increment, decrement, reset } = useCounter()
// 用法二:从100开始计数
const { count: score, increment: addScore } = useCounter(100)
</script>
看到了吗?这就是普通的JavaScript函数调用和解构赋值。没有黑魔法,没有隐式注入,每个变量从哪来,一目了然。
再来一个更贴近实际开发的例子------封装一个通用的"接口请求"组合函数:
go
// 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 }
}
在组件中使用:
go
<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?
go
你的场景是什么?
│
▼
┌───────────────┐ 是 ┌──────────────────────┐
│ 简单的小组件? ├────────→│ 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的体验!
关注 「前端达人」,和数万前端开发者一起进阶,每周分享最实用的前端干货 🚀