Vue3 组合式函数:让你的代码复用如丝般顺滑

在 Vue3 的生态中,组合式 API 无疑非常重要,而组合式函数(Composables)则更加引人注目,今天我想和大家深入探讨组合式函数的魅力,看看它如何让我们的代码复用变得如此优雅。

从一个痛点开始:代码复用的困境

在前端开发中,我们经常会遇到这样的场景:多个组件需要实现相同的功能。比如,一个数据可视化组件需要跟踪鼠标位置,一个交互组件也需要跟踪鼠标位置。如果我们在每个组件里都写一遍相同的逻辑,不仅冗余,还会让后续维护变成噩梦。

在 Vue2 的 Options API 时代,我们可能会用混入(mixin)来解决这个问题,但混入带来的命名冲突、逻辑来源不清晰等问题,始终是开发者心中的一根刺。

而 Vue3 的组合式函数,正是为解决这类问题而生。

什么是组合式函数?

简单来说,组合式函数是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数

它和我们平时写的工具函数(比如格式化时间的函数)最大的区别在于:组合式函数管理的是有状态逻辑------ 也就是那些会随着时间变化的数据和相关操作。

用鼠标跟踪器理解组合式函数

让我们从一个简单的例子入手:跟踪鼠标位置。

如果我们直接在组件中实现这个功能,代码会是这样的:

xml 复制代码
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

// 跟踪鼠标位置的状态
const x = ref(0)
const y = ref(0)

// 更新鼠标位置的方法
function update(event) {
  x.value = event.pageX
  y.value = event.pageY
}

// 挂载时添加事件监听
onMounted(() => window.addEventListener('mousemove', update))
// 卸载时移除事件监听
onUnmounted(() => window.removeEventListener('mousemove', update))
</script>

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

这段代码实现了功能,但如果另一个组件也需要这个功能,我们就得复制粘贴一遍。这显然不是我们想要的。

现在,我们把它改造成组合式函数:

javascript 复制代码
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

// 按照惯例,组合式函数以"use"开头
export function useMouse() {
  // 封装内部状态
  const x = ref(0)
  const y = ref(0)

  // 封装内部方法
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // 封装生命周期逻辑
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // 暴露状态
  return { x, y }
}

然后在任何组件中使用它:

xml 复制代码
<script setup>
import { useMouse } from './mouse.js'

// 只需一行代码,就能获得鼠标跟踪功能
const { x, y } = useMouse()
</script>

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

是不是瞬间清爽了很多?这就是组合式函数的魔力 ------将复杂的有状态逻辑封装成可复用的函数,让组件代码更简洁,逻辑更清晰。

组合式函数的进阶技巧:处理响应式输入

组合式函数的威力不止于此。它还能轻松处理响应式输入,让逻辑复用更加灵活。

我们以一个数据请求的组合式函数useFetch为例。首先实现一个基础版本:

javascript 复制代码
// 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 }
}

这个版本的问题是:如果url变化了,它不会重新发起请求。在实际开发中,我们经常需要根据响应式数据(比如路由参数、组件 props)的变化来重新请求数据。

如何让useFetch支持响应式的url呢?我们可以用watchEffecttoValue来实现:

javascript 复制代码
// fetch.js
import { ref, watchEffect, toValue } from 'vue'

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

  watchEffect(() => {
    // 重置状态
    data.value = null
    error.value = null

    // toValue会处理各种类型的输入:
    // - 如果是ref,取其.value
    // - 如果是函数,执行并取返回值
    // - 否则直接返回
    fetch(toValue(url))
      .then(res => res.json())
      .then(json => data.value = json)
      .catch(err => error.value = err)
  })

  return { data, error }
}

现在,useFetch可以支持多种类型的url参数:

  1. 静态字符串:
kotlin 复制代码
const { data } = useFetch('/api/posts')
  1. 响应式 ref:
csharp 复制代码
const url = ref('/api/posts')
const { data } = useFetch(url)

// 当url变化时,会自动重新请求
url.value = '/api/comments'
  1. getter 函数(常用于依赖其他响应式数据):
javascript 复制代码
// 当props.id变化时,自动重新请求
const { data } = useFetch(() => `/api/posts/${props.id}`)

toValue是 Vue3.3 新增的 API,它完美解决了组合式函数接收不同类型参数的问题,让函数的使用更加灵活。而watchEffect则确保了在依赖变化时,逻辑会重新执行。

要写出高质量的组合式函数,有一些约定和最佳实践需要遵循:

命名规范

组合式函数应该以use开头,采用驼峰命名法,比如useMouseuseFetchuseLocalStorage。这样一眼就能看出这是一个组合式函数,方便识别和使用。

输入参数处理

尽量让组合式函数能够处理各种类型的输入参数(原始值、ref、getter),可以使用toValue来统一处理:

javascript 复制代码
import { toValue } from 'vue'

function useFeature(maybeRefOrGetter) {
  const value = toValue(maybeRefOrGetter)
  // ...
}

副作用管理

如果组合式函数中包含副作用(如事件监听、定时器、请求等),一定要在合适的时机清理它们,避免内存泄漏:

scss 复制代码
function useTimer() {
  const count = ref(0)
  const timer = ref(null)

  function start() {
    timer.value = setInterval(() => {
      count.value++
    }, 1000)
  }

  function stop() {
    clearInterval(timer.value)
  }

  // 组件卸载时停止定时器
  onUnmounted(stop)

  return { count, start, stop }
}

使用限制

组合式函数只能在<script setup>setup()钩子中调用,并且只能同步调用。在某些情况下,也可以在生命周期钩子(如onMounted)中调用。

这是因为组合式函数需要访问当前活跃的组件实例,才能正确注册生命周期钩子和响应式依赖。

总结

组合式函数是 Vue3 组合式 API 的精华所在,它让我们能够像搭积木一样组合和复用逻辑,极大地提升了代码的可维护性和复用性。

通过封装有状态逻辑,组合式函数让我们的组件变得更简洁,逻辑更清晰。它解决了 Vue2 中 mixin 带来的各种问题,为大型应用的开发提供了更优雅的解决方案。

欢迎在评论区分享你在使用组合式函数时的心得和技巧,让我们一起进步!

相关推荐
前端小巷子几秒前
深入理解TCP协议
前端·javascript·面试
万少1 分钟前
鸿蒙外包的十大生存法则
前端·后端·面试
顽疲16 分钟前
从零用java实现 小红书 springboot vue uniapp(13)模仿抖音视频切换
java·vue.js·spring boot
江号软件分享1 小时前
有效保障隐私,如何安全地擦除电脑上的敏感数据
前端
web守墓人2 小时前
【前端】ikun-markdown: 纯js实现markdown到富文本html的转换库
前端·javascript·html
Savior`L2 小时前
CSS知识复习5
前端·css
许白掰2 小时前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器
中微子6 小时前
🔥 React Context 面试必考!从源码到实战的完整攻略 | 99%的人都不知道的性能陷阱
前端·react.js
中微子7 小时前
React 状态管理 源码深度解析
前端·react.js