在 Vue2 时代,组件逻辑复用一直是开发中的一个重点问题。随着项目规模不断扩大,我们经常会遇到这样的情况:
例如用户列表页面:
获取用户数据
分页处理
搜索条件管理
加载状态管理
订单页面:
获取订单数据
分页处理
搜索条件管理
加载状态管理
商品页面:
获取商品数据
分页处理
搜索条件管理
加载状态管理
会发现大量逻辑其实是重复的。
如果直接复制粘贴代码:
页面越来越多
维护成本越来越高
代码重复严重
为了实现逻辑复用,Vue2曾提供过多种解决方案:
但是这些方案都存在一定的问题。
Vue3推出Composition API后,官方推荐使用一种新的逻辑复用方案:
组合式函数(Composables)。
如今在Vue3生态中:
都大量采用组合式函数设计思想。
可以说,组合式函数已经成为Vue3项目开发中的核心能力之一。
一、什么是组合式函数
组合式函数(Composables)本质上就是:
一个利用Vue组合式API封装状态逻辑的函数。
简单理解:
把可复用逻辑抽取到一个函数中
然后在多个组件中共享使用
例如:
import { ref } from 'vue'
export function useCounter() {
const count = ref(0)
const increment = () => {
count.value++
}
return {
count,
increment
}
}
组件中:
const { count, increment } = useCounter()
这样就完成了逻辑复用。
二、为什么需要组合式函数
Options API时代的问题
Vue2中:
data
methods
computed
watch
按照功能分类。
例如:
用户管理逻辑
订单管理逻辑
权限管理逻辑
代码会分散在多个选项中。
data里面一部分
methods里面一部分
watch里面一部分
当业务复杂后:
查找困难
维护困难
阅读困难
Composition API的优势
Vue3采用:
功能聚合
组织代码。
例如:
用户逻辑
全部放一起
订单逻辑
全部放一起
代码结构更清晰。
而组合式函数则进一步实现:
逻辑抽离
逻辑复用
逻辑共享
三、组合式函数的命名规范
官方推荐:
useXXX
例如:
useCounter
useUser
usePagination
useRequest
useTable
useForm
这种命名方式有两个作用。
第一:
一眼就知道是组合式函数。
第二:
方便团队统一规范。
企业项目中几乎都会遵循这个约定。
四、编写第一个组合式函数
创建文件
composables/useCounter.js
编写逻辑
import { ref } from 'vue'
export function useCounter() {
const count = ref(0)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
return {
count,
increment,
decrement
}
}
使用
<script setup>
import { useCounter } from '@/composables/useCounter'
const {
count,
increment,
decrement
} = useCounter()
</script>
模板:
<div>{{ count }}</div>
<button @click="increment">
+
</button>
<button @click="decrement">
-
</button>
五、组合式函数中的响应式状态
组合式函数最大的特点就是:
能够直接使用Vue响应式系统。
例如:
import {
ref,
computed
} from 'vue'
使用ref
const count = ref(0)
使用reactive
const form = reactive({
username: '',
password: ''
})
使用computed
const total = computed(() => {
return price.value * num.value
})
使用watch
watch(keyword, () => {
loadData()
})
组合式函数中可以使用几乎所有Composition API。
六、参数传递
组合式函数支持参数。
例如:
export function useFetch(url) {
}
使用:
useFetch('/api/user')
实际案例
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
return {
count
}
}
调用:
const { count } = useCounter(100)
结果:
count = 100
七、返回值设计
组合式函数通常返回对象。
例如:
return {
data,
loading,
error,
refresh
}
组件使用:
const {
data,
loading,
error,
refresh
} = useRequest()
这种结构非常清晰。
八、封装网络请求
这是企业开发中最常见的场景。
普通写法
每个页面:
loading
error
data
请求逻辑
重复编写。
封装为组合式函数
export function useRequest(api) {
}
内部:
loading管理
错误处理
数据管理
刷新功能
统一封装。
组件中:
const {
data,
loading,
refresh
} = useRequest(getUserList)
代码量大幅减少。
九、封装分页逻辑
后台管理系统中最常见。
例如:
current
pageSize
total
几乎每个列表页都有。
封装:
export function usePagination() {
}
内部管理:
当前页
页大小
总条数
页码切换
页面中:
const {
current,
pageSize,
total,
onPageChange
} = usePagination()
直接使用。
十、封装表单逻辑
例如:
新增用户
编辑用户
查看用户
都会使用表单。
组合式函数:
useForm()
负责:
表单数据
重置
提交
校验
统一管理。
十一、生命周期中的使用
组合式函数内部也可以使用生命周期。
例如:
import {
onMounted
} from 'vue'
onMounted(() => {
loadData()
})
组件调用后:
生命周期会自动注册到当前组件实例。
常见生命周期
onMounted
onUpdated
onUnmounted
onActivated
onDeactivated
都可以使用。
十二、封装鼠标监听案例
例如获取鼠标位置。
const x = ref(0)
const y = ref(0)
监听:
mousemove
更新坐标。
组件中:
const {
x,
y
} = useMouse()
即可直接获取鼠标位置。
这是官方最经典的案例之一。
十三、副作用清理
组合式函数经常会注册:
事件监听
定时器
WebSocket
必须清理。
例如:
window.addEventListener()
离开页面:
removeEventListener()
正确写法:
onUnmounted(() => {
})
进行释放。
否则可能出现:
内存泄漏
重复监听
性能下降
十四、组合式函数与Mixins对比
Mixins存在的问题
多个Mixin同时使用:
data冲突
methods冲突
命名冲突
来源不明确。
例如:
this.loadData()
根本不知道来自哪个Mixin。
Composables优势
显式导入:
import { useUser }
来源清晰。
使用:
const {
loadData
} = useUser()
逻辑关系非常明确。
十五、VueUse与组合式函数
Vue生态中最著名的组合式函数库:
VueUse。
它提供了大量现成工具。
例如:
useMouse
useWindowSize
useStorage
useClipboard
useDark
useFullscreen
开箱即用。
学习VueUse也是学习组合式函数设计思想的最佳方式。
十六、组合式函数最佳实践
单一职责原则
一个组合式函数只做一件事。
正确:
useUser
负责用户逻辑。
错误:
useEverything
什么都放进去。
返回值保持简洁
不要暴露无关数据。
只返回需要的数据和方法。
命名统一
统一:
useXXX
规范。
注意副作用清理
监听器和定时器必须释放。
不要过度封装
简单逻辑无需抽取。
否则会增加理解成本。
十八、总结
组合式函数是Vue3实现逻辑复用的核心方案,也是Composition API思想的最佳实践。
它本质上是一个封装了响应式状态和业务逻辑的普通函数,通过组合各种Composition API,将原本分散在组件中的代码抽离出来,实现高内聚、低耦合的代码结构。
相比Vue2时代的Mixins,组合式函数拥有更好的可维护性、更清晰的逻辑来源、更灵活的组合能力以及更强的TypeScript支持。
在现代Vue3企业级项目中,几乎所有公共业务逻辑都会被封装成组合式函数,例如网络请求、分页管理、表单处理、权限控制、主题切换、缓存管理等。