十一、组合式函数

📅 2026/8/13 23:44:18
十一、组合式函数
一、组合式函数简单来说组合式函数就是一个“带着 Vue 超能力的逻辑胶囊”。在以前Vue 2 时代如果两个页面都需要“鼠标追踪”或者“分页”的功能我们通常会把代码复制粘贴一遍。如果功能复杂代码就会变得像一团乱麻俗称“意大利面条代码”。组合式函数就是为了解决这个问题诞生的。它允许我们把有状态的逻辑比如随时间变化的数据、定时器、网络请求等从组件中抽离出来封装成一个独立的函数。哪里需要就在哪里调用它。官方约定它的名字通常以use开头比如useMouse鼠标追踪、useFetch数据请求。二、核心特征与最佳实践组合式函数之所以强大是因为它具备以下几个核心特征自带 Vue 超能力它不是普通的 JS 函数它内部可以使用 Vue 的响应式 API如ref、reactive和生命周期钩子如onMounted。状态独立每个组件调用这个函数时都会获得一份独立的状态副本。A 组件和 B 组件同时使用useCounter它们的计数是完全互不干扰的。自动绑定生命周期你在组合式函数里写的onMounted或onUnmounted会自动绑定到调用它的那个组件上。当组件销毁时它也会自动清理比如清除定时器不用担心内存泄漏。像搭积木一样组合一个组合式函数可以调用另一个组合式函数通过组合较小的逻辑单元来构建出复杂的业务逻辑。三、函数声明与使用方式一基础封装适合单一功能复用场景我们在开发后台管理系统时经常需要用到“计数器”或“开关切换”功能。我们可以把它抽离出来。第一步声明组合式函数新建一个useCounter.js文件// useCounter.jsimport{ref,computed}fromvue// 约定俗成以 use 开头exportfunctionuseCounter(initialValue0){// 1. 声明响应式状态constcountref(initialValue)// 2. 封装操作方法constincrement()count.valueconstdecrement()count.value--// 3. 还可以封装计算属性constdoubleCountcomputed(()count.value*2)// 4. 将状态和方法打包成一个对象返回return{count,doubleCount,increment,decrement}}第二步在组件中使用!-- MyComponent.vue -- script setup // 1. 导入组合式函数 import { useCounter } from ./useCounter.js // 2. 调用并解构出需要的变量和方法 const { count, doubleCount, increment } useCounter(10) /script template p当前数量: {{ count }}/p p双倍数量: {{ doubleCount }}/p button clickincrement1/button /template方式二嵌套组合与副作用管理适合复杂逻辑场景我们要监听窗口大小变化或者监听某个 DOM 元素的点击事件。这涉及到添加和移除事件监听器副作用。我们可以先封装一个底层的监听函数再组合出高层的业务函数。第一步声明底层的事件监听函数useEventListener.js// useEventListener.jsimport{onMounted,onUnmounted}fromvueexportfunctionuseEventListener(target,event,callback){// 组件挂载时添加监听onMounted(()target.addEventListener(event,callback))// 组件卸载时自动清理防止内存泄漏这是组合式函数的巨大优势onUnmounted(()target.removeEventListener(event,callback))}第二步组合出鼠标位置追踪函数useMouse.js// useMouse.jsimport{ref}fromvueimport{useEventListener}from./useEventListener.jsexportfunctionuseMouse(){constxref(0)constyref(0)// 直接调用底层的组合式函数代码变得极其清爽useEventListener(window,mousemove,(event){x.valueevent.pageX y.valueevent.pageY})return{x,y}}第三步在组件中优雅地使用!-- CursorPosition.vue -- script setup import { useMouse } from ./useMouse.js // 仅仅一行代码就拥有了追踪鼠标位置的全部能力 const { x, y } useMouse() /script template p鼠标坐标: X: {{ x }}, Y: {{ y }}/p /template四、组合式函数与普通函数对比很多新手会疑惑这不就是个普通的 JS 函数吗其实它们有本质的区别。普通函数就像一个“榨汁机”。你扔进去苹果输入它立刻给你一杯苹果汁输出。它是无状态的不关心时间也不关心 Vue 页面。比如formatDate()格式化时间、add(a, b)求和。组合式函数就像一个“带生命的机器人”。它内部有状态比如用ref记录的数据还能感知生命周期知道什么时候该启动什么时候该销毁。比如useMouse()它会持续记录鼠标位置并在组件销毁时自动清理监听。特性普通函数组合式函数 (Composables)状态无状态纯计算有状态使用ref/reactive生命周期无法使用可使用onMounted等钩子响应式无法触发页面更新状态改变会自动触发视图更新调用位置任何地方都可以只能在script setup或setup()顶层同步调用五、使用场景什么时候该把代码抽成组合式函数记住这个口诀“只要是跨组件复用的、有状态的逻辑都可以抽”复杂的数据请求比如封装一个useFetch(url)自动处理加载中loading、成功data、失败error的状态。DOM 事件与交互比如鼠标追踪useMouse()、窗口大小监听useWindowSize()、防抖输入useDebounceInput()。本地存储封装一个useLocalStorage(key, defaultVal)自动把数据同步到浏览器的 LocalStorage 中。拆分臃肿的组件当一个.vue文件代码太多时把“表单校验逻辑”抽成useForm()把“表格分页逻辑”抽成useTable()主组件就会变得非常清爽。六、避坑指南在实际开发中新手最容易踩以下三个坑❌坑一在条件语句里调用错误if (isAdmin) { const { user } useUser() }原因Vue 需要知道当前是哪个组件在调用条件调用会导致 Vue 找不到组件实例直接报错。✅正确做法永远在最顶层调用把条件作为参数传进去例如const { user } useUser({ enabled: isAdmin })。❌坑二不小心把状态变成了“全局共享”错误把const count ref(0)写在了函数外面的模块顶层。这会导致所有用到这个函数的组件共享同一个count。✅正确做法一定要把ref写在函数内部保证每次调用都是独立的新状态。❌坑三忘记清理副作用错误在onMounted里设置了定时器但组件销毁时没清除。✅正确做法有借有还再借不难。一定要配合onUnmounted清除定时器或事件监听。七、官方组合式函数分类函数名称核心作用如何使用 (示例)官方/生态useRouter()获取路由实例用于编程式导航如跳转到其他页面。const router useRouter()router.push(/home)官方/生态useRoute()获取当前路由信息如获取 URL 参数、查询字符串。const route useRoute()const id route.params.id官方/生态useSlots()获取传递给组件的插槽内容用于在 JS 中动态处理插槽。const slots useSlots()const hasHeader !!slots.header八、VueUse1、VueUse 是什么简单来说VueUse 是一个基于 Vue 3 组合式 API 的高质量工具函数集合。在日常开发中我们经常会遇到很多“非 Vue 核心”但又极其常用的需求比如获取鼠标位置、防抖节流、读写本地存储、复制文本到剪贴板等。以前我们需要自己去写这些繁琐的底层逻辑而 VueUse 把这些浏览器 API 和复杂逻辑全部封装成了响应式的组合式函数让我们可以直接“拿来即用”极大地提高了开发效率。2、怎么使用VueUse 的使用非常简单只需两步第一步安装在你的项目终端运行以下命令以 npm 为例npmi vueuse/core第二步按需引入并使用VueUse 支持按需引入用到哪个函数就导入哪个不会增加多余的打包体积。直接在组件的script setup中导入即可script setup import { useMouse } from vueuse/core // 调用函数获取响应式的鼠标坐标 const { x, y } useMouse() /script template p鼠标位置X: {{ x }}, Y: {{ y }}/p /template3、有哪些常用的组合式函数VueUse 提供了 200 多个函数涵盖了前端开发的方方面面。我为你挑选了几个在实际开发中最高频、最实用的函数1.useMouse鼠标追踪作用实时获取鼠标在页面上的坐标位置返回响应式的x和y变量。场景实现鼠标跟随特效、自定义光标、或者在特定区域显示悬浮提示框。script setup import { useMouse } from vueuse/core // 解构出 x 和 y它们会随鼠标移动自动更新 const { x, y } useMouse() /script template p鼠标当前坐标X: {{ x }}, Y: {{ y }}/p /template2.useLocalStorage/useSessionStorage本地存储作用把浏览器的本地存储变成响应式数据。你修改这个变量的值它会自动保存到浏览器中刷新页面后数据依然还在。场景保存用户的登录 Token、记住密码、保存表单草稿、缓存用户的个性化偏好设置等。script setup import { useLocalStorage } from vueuse/core // 绑定本地存储默认值为 light const theme useLocalStorage(my-theme, light) /script template p当前主题{{ theme }}/p button clicktheme dark切换为暗黑模式/button /template3.useDebounceFn防抖函数作用限制某个函数在指定时间内只能执行一次防止频繁触发。场景搜索框输入时的联想查询、防止按钮被用户疯狂连点、窗口大小改变时的重新布局。script setup import { useDebounceFn } from vueuse/core const handleSearch (keyword) { console.log(正在搜索:, keyword) } // 延迟 500ms 执行如果期间再次触发会重新计时 const debouncedSearch useDebounceFn(handleSearch, 500) /script template input inpute debouncedSearch(e.target.value) placeholder输入搜索内容... / /template4.useClipboard剪贴板操作作用一键复制文本到剪贴板并且自带复制成功的状态反馈copied。场景点击按钮复制邀请链接、复制验证码、复制代码片段。script setup import { ref } from vue import { useClipboard } from vueuse/core const source ref(https://vueuse.org) const { copy, copied, isSupported } useClipboard({ source }) /script template button v-ifisSupported clickcopy() {{ copied ? 已复制 : 复制链接 }} /button /template5.useDark暗黑模式切换作用自动检测系统的深色模式偏好并支持手动切换同时自动将状态持久化到本地存储。场景网站右上角的“白天/黑夜”主题切换按钮实现无缝的主题切换体验。script setup import { useDark, useToggle } from vueuse/core const isDark useDark() // 自动响应系统主题或手动切换 const toggleDark useToggle(isDark) // 生成切换方法 /script template button clicktoggleDark() 当前是 {{ isDark ? 黑夜 : 白天 }} 模式 /button /template6.useFetch网络请求作用将原生的 fetch 请求变成响应式自动帮你管理loading加载中、data数据和error报错状态。场景页面加载时获取文章列表、用户详情等 HTTP 请求省去手写大量样板代码。script setup import { useFetch } from vueuse/core const { data, error, isFetching } useFetch(/api/users).get().json() /script template div v-ifisFetching加载中.../div div v-else-iferror出错了{{ error }}/div ul v-else li v-foruser in data :keyuser.id{{ user.name }}/li /ul /template7.onClickOutside点击外部关闭作用监听鼠标点击如果点击了指定元素的外部区域就会触发回调。场景点击弹窗外面的空白区域时自动关闭弹窗、下拉菜单失去焦点时自动收起。script setup import { ref } from vue import { onClickOutside } from vueuse/core const target ref(null) const showDropdown ref(true) // 当点击 target 元素外部时关闭下拉框 onClickOutside(target, () { showDropdown.value false }) /script template div reftarget v-showshowDropdown我是下拉菜单/div /template