一、为什么要引入 Composition API?------Vue 团队是真的被逼急了 😅
1️⃣ Options API 的"结构性问题",不是写法问题
Options API 最大的问题不是"老",而是当组件复杂后,逻辑会被强行拆散。
来看一个典型业务:用户列表 + 搜索 + 请求 + loading + 错误处理
在 Options API 里,你会得到这样的结构:
js
export default {
data() {
return {
list: [],
loading: false,
keyword: '',
error: null
}
},
methods: {
async fetchList() { /* ... */ },
onSearch() { /* ... */ }
},
watch: {
keyword() { /* 触发搜索 */ }
},
created() {
this.fetchList()
}
}
问题在哪?
- 一个"列表请求"的逻辑,被拆进了
data / methods / watch / 生命周期 - 逻辑的时间顺序 和业务意图被结构打断
- 想复用?复制一整坨 options,再删删改改 😵💫
👉 Vue 官方在 RFC 里就点名了这个问题:
Options API 不利于复杂逻辑的组织与复用。
2️⃣ Composition API 的核心动机:按"逻辑"组织代码
Composition API 的第一性原理只有一句话:
把"相关的逻辑"写在一起,而不是把"同类型的代码"写在一起。
这不是语法升级,这是代码组织方式的升级。
二、逻辑复用问题:Composition API 是怎么"正面硬刚"的?🧠
1️⃣ Options API 的复用方式有多痛?
mixins:历史遗留之痛
js
mixins: [listMixin, searchMixin]
痛点你肯定踩过:
- 命名冲突(methods/data 谁覆盖谁?)
- 来源不透明(这个方法到底从哪来的?)
- 逻辑耦合严重(删一个 mixin,全组件爆炸)
👉 mixin 最大的问题:不是显式依赖。
2️⃣ Composition API:用"函数"做逻辑单元
Composition API 的复用单位不是组件,是 函数(Composable)。
js
export function useList() {
const list = ref([])
const loading = ref(false)
async function fetchList() {
loading.value = true
// ...
loading.value = false
}
return { list, loading, fetchList }
}
这一下,问题全解决了:
- 依赖显式(import 什么就用什么)
- 命名空间天然隔离
- 可组合、可测试、可拆分
👉 逻辑像乐高,而不是像面团。
三、对比 Options API:不是"谁淘汰谁",而是"谁更适合复杂度"⚖️
1️⃣ 写法对比(同一个功能)
Options API(逻辑被拆散)
js
data() { return { count: 0 } }
methods: {
inc() { this.count++ }
}
Composition API(逻辑内聚)
js
const count = ref(0)
const inc = () => count.value++
看起来只是"换个地方写",但一旦功能多起来,差距会指数级放大。
2️⃣ 思维模型对比
| 维度 | Options API | Composition API |
|---|---|---|
| 组织方式 | 按配置类型 | 按业务逻辑 |
| 复用能力 | mixins(弱) | composables(强) |
| 类型推导 | 困难 | 天生友好 |
| 可拆分性 | 差 | 极强 |
| 大型项目 | 易失控 | 更可维护 |
👉 Options API 不是不能用,而是复杂度一高就很吃力。
四、可维护性分析:Composition API 为什么更适合"活得久"的项目?🧩
1️⃣ 可维护性 = 可理解性 + 可修改性
Composition API 在这两点上优势非常明显:
✅ 逻辑集中
js
// useSearch.ts
export function useSearch(fetcher) {
const keyword = ref('')
watch(keyword, () => fetcher(keyword.value))
return { keyword }
}
谁看这段代码都知道:
"哦,这是搜索逻辑。"
2️⃣ 对 TypeScript 极其友好(这是 Vue3 的关键战场)
ts
export function useUser() {
const user = ref<User | null>(null)
function setUser(u: User) {
user.value = u
}
return { user, setUser }
}
- 自动推导
- IDE 友好
- 重构安全
👉 Vue3 + Composition API + TS,不是噱头,是工程现实。
五、实战重构示例:把一个"难维护组件"拆干净 💪
1️⃣ 原始 Options API(节选)
js
export default {
data() {
return {
list: [],
loading: false,
keyword: ''
}
},
methods: {
async fetchList() { /* ... */ }
},
watch: {
keyword() {
this.fetchList()
}
},
mounted() {
this.fetchList()
}
}
问题:
- 请求逻辑 + 搜索逻辑 + 状态全混在一起
- 无法复用
- 测试困难
2️⃣ Composition API 重构(分而治之)
useList.ts
ts
export function useList() {
const list = ref([])
const loading = ref(false)
async function fetchList(keyword = '') {
loading.value = true
// mock fetch
list.value = []
loading.value = false
}
return { list, loading, fetchList }
}
useSearch.ts
ts
export function useSearch(onSearch) {
const keyword = ref('')
watch(keyword, () => onSearch(keyword.value))
return { keyword }
}
组件中组合
ts
const { list, loading, fetchList } = useList()
const { keyword } = useSearch(fetchList)
onMounted(() => fetchList())
✨ 重构后的收益:
- 每段逻辑职责单一
- 可单独测试
- 可跨组件复用
- 阅读成本直线下降
六、一句"醒脑总结"(送你当文章结尾 😏)
Composition API 从来不是为了"写得更高级",
而是为了回答一个更现实的问题:
当你的业务越来越复杂时,你的代码还能不能被人看懂?
所以我最后反问你一句:
如果一年后是你自己来维护这段代码,你希望它长得像 Options API,还是像 Composition API? 😉