Vue 3 的 Composable 是基于 Composition API 的可复用逻辑封装函数,接收参数、使用响应式 API、返回响应式对象;支持异步、副作用与清理;需遵循命名规范、避免响应性丢失,并按领域组织工程化。

Vue 3 的 Composition API + setup() + 自定义 Hook(即 composable)天然支持逻辑复用,写法和 React Hooks 非常相似——但不是照搬,而是遵循 Vue 响应式范式。核心就一条:把与响应式状态、生命周期、副作用相关的逻辑,封装成可导入、可复用的函数,返回响应式数据或操作方法。
一、基础结构:一个标准的 Vue3 Composable 长什么样?
它就是一个普通 JavaScript 函数,接收参数(可选),内部使用 ref、reactive、onMounted 等 API,最后返回对象(含响应式变量、计算属性、方法等)。
✅ 正确示例(封装一个计数器逻辑):
src/composables/useCounter.jsimport { ref, computed } from 'vue'
<p>export function useCounter(initial = 0) {
const count = ref(initial)
const double = computed(() => count.value * 2)</p><p>const increment = () => count.value++
const decrement = () => count.value--</p><p>return {
count,
double,
increment,
decrement
}
}</p>
✅ 在组件中使用:
Element Plus Vue3 桌面组件库。在开发 Vue 3.x 页面时调用,使用 Element Plus 组件(如 el-button、el-input、el-form、el-table 等)。
<script setup>
import { useCounter } from '@/composables/useCounter'
<p>const { count, double, increment, decrement } = useCounter(5)
</script>
二、进阶技巧:如何处理异步、副作用和清理?
React 中有 useEffect 和 useCallback;Vue 中对应的是 watch、watchEffect、onBeforeUnmount 等生命周期钩子,它们可在 composable 内部直接调用。
✅ 示例:封装一个带 loading 和错误处理的请求 Hook
src/composables/useApi.jsimport { ref, onBeforeUnmount } from 'vue'
import axios from 'axios'
<p>export function useApi(url) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)</p><p>const fetchData = async () => {
loading.value = true
error.value = null
try {
const res = await axios.get(url)
data.value = res.data
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
}</p><p>// 可选:自动在组件卸载前取消未完成请求(需 axios 配置 cancelToken 或用 AbortController)
const controller = new AbortController()
onBeforeUnmount(() => controller.abort())</p><p>return {
data,
loading,
error,
fetchData
}
}</p>
三、避免常见坑:命名、响应式穿透、参数响应化
-
命名统一用
useXxx:这是 Vue 官方约定,便于 IDE 识别和开发者理解 -
不要直接返回解构后的响应式值(如
return { ...toRefs(obj) }),会丢失响应性;正确做法是返回ref或reactive对象,或用toRefs包裹后返回 -
参数如果是响应式对象(比如
props),别直接监听props.xxx,要用watch(() => props.xxx, ...)或computed(() => props.xxx),否则不会响应更新 -
多个 Hook 共享状态?用
provide/inject或全局 store,不要试图让两个 useXxx 直接共享 ref —— 这违背封装原则
四、工程化建议:组织你的业务 Hooks 库
- 按领域分包:
useAuth、useTable、useForm、useWebSocket - 每个 Hook 单独文件,导出命名函数(方便单元测试 & Tree-shaking)
- 复杂 Hook 可拆分子函数(如
useTable内部调用usePagination、useSort) - 提供 TypeScript 类型定义,尤其泛型参数(如
useApi)(url: string)
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










