一、Vue 2的痛:谁没在data和methods之间迷过路?
还记得那些年,我们写Vue 2组件时的场景吗?
export default {
data() {
return {
count: 0,
message: 'Hello',
user: { name: '张三', age: 18 }
}
},
methods: {
increment() { this.count++ },
showMessage() { console.log(this.message) },
updateUser() { /* 翻半天找到user在哪 */ }
},
mounted() {
// 这里又想用data里的变量,又想调methods里的方法...
}
}
这种Options API的写法,就像把衣服、鞋子、零食全扔进一个衣柜——找东西全靠缘分!相关逻辑被强行拆散到不同选项里,组件一复杂,代码就乱成“意大利面”。
二、Vue 3 组合API:给你的代码做个“整理收纳”
Vue 3的组合API(Composition API)核心思想就一句话:把相关的逻辑放一起!
想象一下,你要做个计数器功能:
- 数据:
count - 方法:
increment、decrement - 生命周期:组件挂载时从本地存储读取初始值
在Vue 2里,这些要分散在data、methods、mounted里。而在Vue 3中,你可以这样:
import { ref, onMounted } from 'vue'
// 把计数器相关的所有逻辑封装到一个函数里
function useCounter() {
const count = ref(0)
function increment() { count.value++ }
function decrement() { count.value-- }
onMounted(() => {
const saved = localStorage.getItem('count')
if (saved) count.value = parseInt(saved)
})
// 返回所有需要暴露的东西
return { count, increment, decrement }
}
这就叫组合函数——把零散的逻辑打包成独立的、可复用的单元!
三、实战:用组合API重构用户管理功能
假设我们要做一个用户列表,支持添加、删除、搜索。看看组合API如何让代码更清晰:
<template>
<div>
<input v-model="searchKeyword" placeholder="搜索用户">
<button @click="addUser">添加随机用户</button>
<ul>
<li v-for="user in filteredUsers" :key="user.id">
{{ user.name }}
<button @click="removeUser(user.id)">删除</button>
</li>
</ul>
<p>用户总数:{{ userStats.total }}</p>
<p>男性用户:{{ userStats.maleCount }}</p>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
// 用户数据逻辑
function useUsers() {
const users = ref([])
const addUser = () => {
const newUser = {
id: Date.now(),
name: `用户${users.value.length + 1}`,
gender: Math.random() > 0.5 ? 'male' : 'female'
}
users.value.push(newUser)
}
const removeUser = (id) => {
users.value = users.value.filter(user => user.id !== id)
}
// 模拟初始化数据
onMounted(() => {
users.value = [
{ id: 1, name: '张三', gender: 'male' },
{ id: 2, name: '李四', gender: 'female' }
]
})
return { users, addUser, removeUser }
}
// 用户统计逻辑
function useUserStats(users) {
const userStats = computed(() => {
const total = users.value.length
const maleCount = users.value.filter(u => u.gender === 'male').length
return { total, maleCount }
})
return { userStats }
}
// 搜索逻辑
function useSearch(users) {
const searchKeyword = ref('')
const filteredUsers = computed(() => {
if (!searchKeyword.value) return users.value
return users.value.filter(user =>
user.name.includes(searchKeyword.value)
)
})
return { searchKeyword, filteredUsers }
}
export default {
setup() {
const { users, addUser, removeUser } = useUsers()
const { userStats } = useUserStats(users)
const { searchKeyword, filteredUsers } = useSearch(users)
return {
users: filteredUsers, // 使用过滤后的用户列表
addUser,
removeUser,
searchKeyword,
userStats
}
}
}
</script>
看到魔法了吗?我们把一个复杂功能拆成了三个独立的逻辑块:
useUsers:管理用户数据useUserStats:计算用户统计useSearch:处理搜索过滤
每个函数只关心自己的事情,这就是单一职责原则的完美体现!
四、组合API的超级技能:超越mixin的代码复用
Vue 2里我们常用mixin复用代码,但mixin有很多问题:
- 命名冲突:多个mixin可能定义相同的属性名
- 来源不明:一个组件用了多个mixin后,很难知道某个方法来自哪个mixin
- 关系复杂:mixin之间可能相互依赖
组合API完美解决了这些问题:
// 复用鼠标位置跟踪功能
function useMouse() {
const x = ref(0)
const y = ref(0)
function update(e) {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
return { x, y }
}
// 复用本地存储功能
function useLocalStorage(key, initialValue) {
const data = ref(initialValue)
// 读取已有值
const stored = localStorage.getItem(key)
if (stored) data.value = JSON.parse(stored)
// 自动保存
watch(data, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
return data
}
// 在组件中使用
export default {
setup() {
const { x, y } = useMouse()
const settings = useLocalStorage('settings', { theme: 'light' })
return { x, y, settings }
}
}
这种复用方式清晰明了:你知道每个数据来自哪个函数,命名冲突可以通过重命名解决,TypeScript支持也更好。
五、组合API的进阶玩法:让代码更优雅
1. 响应式状态管理
import { reactive, toRefs } from 'vue'
function useForm() {
const state = reactive({
username: '',
password: '',
errors: {}
})
function validate() {
state.errors = {}
if (!state.username) state.errors.username = '用户名不能为空'
if (state.password.length < 6) state.errors.password = '密码太短'
}
// 使用toRefs保持响应式
return { ...toRefs(state), validate }
}
2. 异步操作处理
function useApi(endpoint) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
async function fetchData() {
loading.value = true
error.value = null
try {
const response = await fetch(endpoint)
data.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
onMounted(fetchData)
return { data, loading, error, refetch: fetchData }
}
六、迁移指南:从Vue 2平稳过渡
如果你有Vue 2项目,别急着重写!可以这样逐步迁移:
- 混合使用:Vue 3支持同时使用Options API和Composition API
- 从新功能开始:在新功能或重构时尝试组合API
- 使用工具:Vue官方提供了迁移工具和兼容版本
// 渐进式迁移示例
export default {
// 旧的Options API
data() {
return { oldData: '我还在' }
},
methods: {
oldMethod() { console.log('老方法') }
},
// 新的Composition API
setup() {
const { newData, newMethod } = useNewFeature()
return { newData, newMethod }
}
}
七、总结:为什么组合API是未来?
组合API不是要完全取代Options API,而是提供了更好的代码组织方式:
- 逻辑复用:像搭积木一样组合功能
- 类型推导:TypeScript支持杠杠的
- 代码组织:相关代码在一起,维护更轻松
- 灵活性:不受组件结构限制,自由组合
就像从整理杂乱的衣柜变成使用分类收纳盒,一开始可能需要适应,但一旦习惯,就再也回不去了!
最后的小贴士:组合API的学习曲线确实比Options API陡峭,但投入时间绝对值得。建议从小功能开始尝试,慢慢体会它的美妙之处。记住,好的代码不是写出来的,是“组合”出来的!

被折叠的 条评论
为什么被折叠?



