当前位置: 首页 > news >正文

Vue3核心语法进阶(Hook)

Vue3 自定义 Hook:让你的代码像乐高一样“可复用”!

大家好,我是你们的前端小伙伴!上一篇我们聊了 Vue3 的生命周期,今天咱们继续深入 Vue3 的核心利器——自定义 Hook(Custom Hook)

如果你已经用过 refreactivewatchonMounted 这些 Composition API,那你已经“会用工具了”。而今天我们要学的,是如何把一堆工具打包成一个“超级工具包”,哪里需要就往哪里一放,功能立马就有了!

这个“超级工具包”,就是 自定义 Hook


什么是自定义 Hook?

想象一下,你在写一个电商网站,有 5 个页面都需要“获取用户信息 + 检查登录状态 + 显示用户头像”。

如果你在每个页面都重复写一遍:

const userInfo = ref(null)
const isLoggedIn = ref(false)onMounted(async () => {const data = await fetch('/api/user')userInfo.value = dataisLoggedIn.value = data ? true : false
})watch(userInfo, (newVal) => {if (newVal) console.log('用户已登录')
})

那……恭喜你,代码已经“复制粘贴”了 5 遍!如果哪天接口变了,你得改 5 个地方,想想都头大。

自定义 Hook 就是:把这段“通用逻辑”抽出来,变成一个函数,谁需要谁调用!


自定义 Hook 的本质

它就是一个以 use 开头的普通函数,比如 useUseruseCartuseLocalStorage

它内部可以使用任何 Composition API(refwatchonMounted 等),然后把需要暴露的数据和方法 return 出去。


动手写一个:useUser() 用户信息 Hook

我们来写一个真正可用的自定义 Hook!

// composables/useUser.js
import { ref, onMounted, watch } from 'vue'export function useUser() {const userInfo = ref(null)const isLoggedIn = ref(false)const loading = ref(true)// 模拟请求用户数据const fetchUser = async () => {loading.value = truetry {const res = await fetch('/api/user')const data = await res.json()userInfo.value = dataisLoggedIn.value = !!data} catch (err) {console.error('获取用户失败', err)} finally {loading.value = false}}// 组件挂载时自动获取用户onMounted(() => {fetchUser()})// 监听用户信息变化watch(userInfo, (newVal) => {if (newVal) {console.log(`欢迎回来,${newVal.name}!`)}})// 把你需要的东西 return 出去return {userInfo,isLoggedIn,loading,fetchUser // 也可以手动刷新}
}

文件名建议:composables/useXxx.js,这是 Vue 社区的约定。


在组件中使用它

现在,任何组件只要想用“用户逻辑”,一句话搞定:

<!-- UserProfile.vue -->
<script setup>
import { useUser } from '@/composables/useUser'// 一行代码,搞定用户逻辑!
const { userInfo, isLoggedIn, loading, fetchUser } = useUser()
</script><template><div v-if="loading">加载中...</div><div v-else-if="isLoggedIn"><h2>欢迎,{{ userInfo.name }}!</h2><button @click="fetchUser">刷新</button></div><div v-else>请先登录</div>
</template>

看到了吗?组件代码变得极其干净,逻辑都被“封装”到 useUser 里了。


自定义 Hook 的核心优势

优势说明
逻辑复用一套登录逻辑,10 个页面都能用
解耦清晰组件只管“视图”,Hook 管“逻辑”
易于测试直接测试 useUser 函数,不用渲染组件
类型友好TypeScript 能自动推导返回值类型

高级用法:支持参数和返回函数

自定义 Hook 不只是“固定逻辑”,它也可以很灵活!

示例:useLocalStorage —— 让数据自动存到本地

// composables/useLocalStorage.js
import { ref, watch } from 'vue'export function useLocalStorage(key, initialValue) {// 从 localStorage 读取,或用默认值const data = ref(JSON.parse(localStorage.getItem(key)) || initialValue)// 数据变化时,自动存到 localStoragewatch(data, (newVal) => {localStorage.setItem(key, JSON.stringify(newVal))}, { deep: true }) // deep: true 支持对象/数组return data
}

使用它:

// 计数器,刷新页面也不会丢!
const count = useLocalStorage('count', 0)// 用户设置
const settings = useLocalStorage('userSettings', { theme: 'dark' })

看,一个 Hook,支持任意 key 和默认值,真正做到了“通用”


常见误区 & 注意事项

误区 1:在普通函数里用 ref 就是 Hook?

No!只有在 setup<script setup> 中调用的函数,才能使用 Composition API。你的 useXxx 函数必须在组件上下文中调用。

误区 2:Hook 可以 return 模板?

不能!Hook 只负责逻辑和数据,模板还是得在组件里写。

最佳实践:命名规范

  • 一定要以 use 开头,比如 useMouseuseScrolluseFetch
  • 返回值尽量结构清晰,方便解构使用
  • 复杂逻辑可以拆分成多个小 Hook

实战案例:useMouse —— 跟踪鼠标位置

// composables/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'export function useMouse() {const x = ref(0)const y = ref(0)const update = (e) => {x.value = e.clientXy.value = e.clientY}onMounted(() => {window.addEventListener('mousemove', update)})onUnmounted(() => {window.removeEventListener('mousemove', update)})return { x, y }
}

使用:

<script setup>
import { useMouse } from '@/composables/useMouse'
const { x, y } = useMouse()
</script><template><div>鼠标位置:{{ x }}, {{ y }}</div>
</template>

瞬间,你的组件就有了“追踪鼠标”的超能力!


为什么叫“Hook”?

“Hook” 的意思是“钩子”,它就像是把一段逻辑“钩”进组件的生命周期中。

比如 onMounted 就是一个“钩子”,告诉 Vue:“等组件挂载后,执行我这个函数”。

而自定义 Hook,就是把多个钩子和逻辑打包,变成一个可复用的“增强包”。


总结:自定义 Hook 的灵魂

关键点说明
目的逻辑复用,避免重复代码
形式useXxx() 函数,返回响应式数据和方法
能力可接收参数、可组合多个 API、可嵌套使用
好处代码更清晰、维护更容易、团队协作更高效

最后一句话

组件负责“长什么样”,Hook 负责“怎么动”。

把通用逻辑封装成 Hook,你的代码就会像乐高积木一样,拼装自由,复用无忧

http://www.lryc.cn/news/609526.html

相关文章:

  • 如何使用EF框架操作Sqlite
  • 20250805问答课题-实现TextRank + 问题分类
  • 量子计算接口开发:Python vs Rust 性能对决
  • uniapp快遞上門提貨的時間選擇的插件
  • PyTorch生成式人工智能(25)——基于Transformer实现机器翻译
  • 代码详细注释:(linux)TCP客户端接收服务器端发的信息
  • AI 大模型分类全解析:从文本到多模态的技术图谱
  • Rust ⽣成 .wasm 的极致瘦⾝之道
  • 从 Hive 数仓出发,全面剖析 StarRocks、MySQL、HBase 的使用场景与区别
  • 【Spark征服之路-4.5-Spark-Streaming核心编程(三)】
  • [Oracle] TO_CHAR()函数
  • 安装MySQL教程时可能遇到的问题
  • 【Linux】重生之从零开始学习运维之GTID复制
  • XXE漏洞原理及利用
  • NSS-DAY17 2025SWPU-NSSCTF
  • Chrontel 【CH7103B-B】CH7103B HDMI to YPbPr Converter
  • 行业报告:.games域名正引领游戏娱乐产业营销新风向
  • 力扣 hot100 Day65
  • 嵌入式学习之51单片机——串口(UART)
  • 回归预测 | MATLAB实现BP神经网络多输入单输出回归预测+SHAP可解释分析
  • 分布式光伏气象站:为分散电站装上 “智慧之眼”
  • 零基础掌握 Scrapy 和 Scrapy-Redis:爬虫分布式部署深度解析
  • 分布式版本控制工具Git
  • Spring之【Bean的实例化方式】
  • 电脑忘记开机密码怎么办?【图文详解】5种方法重置/更改/取消/设置开机密码?
  • Java从入门到精通 - 算法、正则、异常
  • 深入浅出 RabbitMQ:简单队列实战指南
  • 【Linux指南】软件安装全解析:从源码到包管理器的进阶之路
  • 小杰数据结构(five day)——知人者智,自知者明。
  • WPF 按钮背景色渐变