一、前言

当 Vue 3 发布时,组合式 API(Composition API) 带来了一个革命性的变化:

我们不再需要依赖 data、methods、computed 这些分散的选项,而是能用函数的方式,灵活组织逻辑。

简单来说:

  • Options API 更像是“配置式”;

  • Composition API 则让我们“像写逻辑一样组织组件”。

组合式函数(Composables) ,就是在这个新体系下,用于封装和复用有状态逻辑的函数。


二、什么是组合式函数?

先来看一句官方定义:

也就是说,它不仅可以处理计算逻辑、请求接口、事件监听,还能和组件生命周期绑定,并且是响应式的。

按照惯例,我们命名时一般以 use 开头

// useXxx 组合式函数命名惯例
export function useMouse() { ... }
export function useFetch() { ... }
export function useEventListener() { ... }

三、基础示例:从组件逻辑到组合式函数

假设我们要做一个“鼠标追踪器”,实时显示鼠标位置。

如果直接写在组件里,可能是这样

<!-- MouseComponent.vue -->
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

// 定义响应式状态
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))
</script>

<template>
  鼠标坐标:{{ x }}, {{ y }}
</template>

很好,但如果我们多个页面都要复用这个逻辑呢?

那就应该把它抽出来!


四、封装成组合式函数

// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

// 约定:组合式函数以 use 开头
export 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 }
}

使用起来非常简单:

<!-- MouseComponent.vue -->
<script setup>
import { useMouse } from './mouse.js'
const { x, y } = useMouse()
</script>

<template>
  鼠标坐标:{{ x }}, {{ y }}
</template>

这样写的好处是:

  • 组件逻辑更清晰;
  • 多处可复用;
  • 生命周期自动关联;
  • 每个组件都拥有独立的状态(互不干扰)。

五、进阶封装:useEventListener

假如我们还想监听滚动、键盘等事件,可以进一步抽象出一个事件监听函数

// event.js
import { onMounted, onUnmounted } from 'vue'

export function useEventListener(target, event, callback) {
  onMounted(() => target.addEventListener(event, callback))
  onUnmounted(() => target.removeEventListener(event, callback))
}

接着 useMouse 就能进一步简化:

// mouse.js
import { ref } from 'vue'
import { useEventListener } from './event'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)

  useEventListener(window, 'mousemove', (e) => {
    x.value = e.pageX
    y.value = e.pageY
  })

  return { x, y }
}

这样我们不仅复用了逻辑,还建立了逻辑的“组合关系” ——

组合式函数可以嵌套调用另一个组合式函数


六、异步场景:useFetch 示例

除了事件逻辑,我们常常需要封装“异步请求逻辑”,比如:

// fetch.js
import { ref } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  fetch(url)
    .then((res) => res.json())
    .then((json) => (data.value = json))
    .catch((err) => (error.value = err))

  return { data, error }
}

使用方式:

<script setup>
import { useFetch } from './fetch.js'

const { data, error } = useFetch('https://api.example.com/posts')
</script>

<template>
  <div v-if="error"> 出错:{{ error.message }}</div>
  <div v-else-if="data"> 数据:{{ data }}</div>
  <div v-else>⏳ 加载中...</div>
</template>

七、响应式参数:动态请求的 useFetch

上面 useFetch 只会执行一次,

但如果我们希望在 URL 改变时自动重新请求呢?

就可以用 watchEffect() + toValue():

// fetch.js
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  const fetchData = () => {
    data.value = null
    error.value = null

    fetch(toValue(url)) // 兼容 ref / getter / 普通字符串
      .then((res) => res.json())
      .then((json) => (data.value = json))
      .catch((err) => (error.value = err))
  }

  watchEffect(() => {
    fetchData() // url 改变时会自动重新执行
  })

  return { data, error }
}

使用示例:

<script setup>
import { ref } from 'vue'
import { useFetch } from './fetch.js'

const postId = ref(1)
const { data, error } = useFetch(() => `/api/posts/${postId.value}`)

// 模拟切换文章
function nextPost() {
  postId.value++
}
</script>

<template>
  <button @click="nextPost">下一篇</button>
  <div v-if="error"> 出错:{{ error.message }}</div>
  <div v-else-if="data"> 文章:{{ data.title }}</div>
  <div v-else>⏳ 加载中...</div>
</template>

这就让你的 useFetch 成为了真正“响应式的请求函数”。


八、组合式函数的使用规范

项目推荐做法原因
命名useXxx()一目了然,符合惯例
返回值返回多个 ref,不要直接返回 reactive 对象防止解构时丢失响应性
生命周期必须在 Vue 需要绑定当前组件实例
️ 参数建议使用 toValue() 规范化输入兼容 ref、getter、普通值
清理要在 onUnmounted() 清理副作用避免内存泄漏

九、与其他模式的比较

模式优点缺点
Mixins逻辑复用简单来源不清晰、命名冲突、隐式依赖
无渲染组件 (Renderless)可复用逻辑 + UI会额外创建组件实例,性能差
组合式函数 (Composables)无实例开销、逻辑清晰、依赖显式不直接提供模板复用

结论:


十、总结

概念说明
组合式函数利用 Vue 组合式 API 封装可复用逻辑的函数
核心特性可使用 ref / reactive / 生命周期钩子 / watch
优势灵活组合、逻辑清晰、性能优秀、类型友好
常见应用请求封装、事件监听、滚动追踪、权限控制、表单管理等
开发建议命名统一、输入规范化、注意生命周期上下文

最后

Composables 就像是 Vue 世界里的「逻辑积木」——

你可以自由拼接、拆解、组合它们,构建出任何复杂的交互逻辑。

如果你曾觉得逻辑在组件里越堆越乱,

那是时候开始用 组合式函数 让代码“呼吸”了。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]