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

Vue3基础知识-Hook实现逻辑复用、代码解耦

一、完整代码示例。

1. 自定义 Hook:hooks/useMousePosition.js


import { reactive, onMounted, onUnmounted } from 'vue'/*
hook本质:一个普通函数,内部利用Vue3的reactive、onMounted等API封装“响应式数据+逻辑+生命周期”最后返回需要暴露的数据或方法。
hook好处:逻辑复用:如果多个组件都需要 “监听鼠标位置”,只需调用 useMousePosition() 即可,无需重复写监听代码。逻辑集中:比Vue 的mixin更清晰,避免命名冲突。响应式联动:hook可以返回响应式对象供其他组件使用
*/// 自定义 hook:命名通常以 use 开头
export default function() {// 1. 响应式数据:存储鼠标坐标const mouse = reactive({x: 0,y: 0})// 2. 方法:更新鼠标坐标function updatePosition(e) {mouse.x = e.pageXmouse.y = e.pageY}// 3. 生命周期钩子:组件挂载后开始监听onMounted(() => {window.addEventListener('mousemove', updatePosition)})// 4. 生命周期钩子:组件卸载前停止监听(避免内存泄漏)。原生事件自行移除onUnmounted(() => {window.removeEventListener('mousemove', updatePosition)})// 5. 暴露响应式数据(供组件使用)return mouse
}

2. 组件使用:App.vue

在组件中引入并调用自定义 Hook,无需关心内部实现,直接使用响应式的鼠标坐标。

<template><div>鼠标位置:x={{ mouse.x }}, y={{ mouse.y }}</div>
</template><script>
// 引入自定义 hookimport useMousePosition from './hooks/useMousePosition'export default {name:'App',setup(){//useMousePosition()用于获取响应式对象,同时其内部的生命周期函数会被绑定当前vc实例上const mouse = useMousePosition()return {mouse}}}
</script>

二、代码解析

1. 自定义 Hook 核心逻辑(useMousePosition.js

(1)响应式数据定义
const mouse = reactive({ x: 0, y: 0 })
  • 使用 reactive 创建响应式对象:Vue3 会通过 Proxy 代理该对象,当 mouse.x/mouse.y 变化时,自动触发依赖更新(如模板重新渲染)。
  • 数据作用域:mouse 是 Hook 内部变量,仅通过返回值暴露给组件,避免全局污染。
(2)事件处理函数
function updatePosition(e) {mouse.x = e.pageXmouse.y = e.pageY
}
  • 接收浏览器 mousemove 事件对象 e,获取 pageX/pageY(鼠标相对于文档左上角的坐标)并更新响应式数据。
  • 该函数是 Hook 内部逻辑,不暴露给组件,实现「封装细节、暴露结果」的设计。
(3)生命周期钩子:绑定与清理事件
// 组件挂载后绑定事件
onMounted(() => {window.addEventListener('mousemove', updatePosition)
})// 组件卸载前清理事件
onUnmounted(() => {window.removeEventListener('mousemove', updatePosition)
})
  • 为什么在 onMounted 绑定?
    组件挂载后(onMounted),DOM 已就绪,此时绑定全局事件(window 上的 mousemove)不会丢失,且能确保事件监听与组件生命周期同步。
  • 为什么必须在 onUnmounted 清理?
    addEventListener 是浏览器原生事件,Vue 不会自动解绑。若不清理,组件卸载后事件仍会触发,导致回调函数引用的组件实例无法被垃圾回收,造成内存泄漏
(4)返回响应式数据
return mouse
  • 仅将需要的响应式数据暴露给组件,隐藏 updatePosition 等内部函数,符合「最小暴露原则」,降低组件与 Hook 的耦合度。

2. 组件使用逻辑(App.vue

(1)引入与调用 Hook
import useMousePosition from './hooks/useMousePosition'setup() {const mouse = useMousePosition()return { mouse }
}
  • 在 setup 中调用 Hook:setup 是 Vue3 组件的「逻辑入口」,此时调用 Hook,Hook 内部的生命周期钩子会自动绑定到当前组件实例(即 App 组件),确保钩子能按组件生命周期触发。
  • 返回数据给模板:mouse 是响应式对象,模板中引用 mouse.x/mouse.y 时,会自动建立依赖,鼠标移动时模板实时更新。
(2)模板渲染
<p class="coordinate">x: {{ mouse.x }}px</p>
<p class="coordinate">y: {{ mouse.y }}px</p>
  • 无需编写任何监听逻辑,直接使用 Hook 返回的响应式数据,实现「数据驱动视图」的 Vue 核心思想。

三、自定义 Hook 的优势(对比传统写法)

假设不用 Hook,直接在组件中写监听逻辑,代码会是这样:

// 无 Hook 的组件写法(冗余、难复用)
setup() {const mouse = reactive({ x: 0, y: 0 })function updatePosition(e) {mouse.x = e.pageXmouse.y = e.pageY}onMounted(() => {window.addEventListener('mousemove', updatePosition)})onUnmounted(() => {window.removeEventListener('mousemove', updatePosition)})return { mouse }
}

若有 3 个组件需要监听鼠标位置,上述代码要复制 3 次,而用 Hook 只需调用 useMousePosition() 即可。对比之下,Hook 的优势显而易见:

  1. 逻辑复用:一份代码,多组件共享,减少重复开发。
  2. 代码解耦:组件只关心「使用数据」,Hook 关心「如何获取数据」,职责分明。
  3. 避免冲突:相比 Vue2 的 mixin,Hook 不会出现命名冲突(数据和方法作用域独立)。

四、扩展:增强 Hook 功能

我们可以轻松扩展这个 Hook 的功能,比如添加「重置鼠标坐标」的方法,让组件能主动控制 Hook 内部逻辑:

// 增强版 useMousePosition.js
export default function useMousePosition() {const mouse = reactive({ x: 0, y: 0 })function updatePosition(e) {mouse.x = e.pageXmouse.y = e.pageY}// 新增:重置鼠标坐标的方法(暴露给组件)function resetPosition() {mouse.x = 0mouse.y = 0}onMounted(() => {window.addEventListener('mousemove', updatePosition)})onUnmounted(() => {window.removeEventListener('mousemove', updatePosition)})// 返回数据 + 方法return { mouse, resetPosition }
}

组件中使用新增方法:

<template><!-- 新增重置按钮 --><button @click="resetPosition">重置坐标</button>
</template><script>
setup() {// 获取方法并返回给模板const { mouse, resetPosition } = useMousePosition()return { mouse, resetPosition }
}
</script>

五、总结

  1. Vue3 自定义 Hook 本质:普通函数 + Composition API(响应式 API + 生命周期钩子),封装可复用逻辑。
  2. 核心规范:以 use 开头命名,仅暴露必要的接口,隐藏内部实现。
  3. 适用场景:任何需要复用的逻辑(如请求数据 useFetch、定时器 useTimer、本地存储 useLocalStorage 等)。

文章转载自:

http://0yiEgAs6.gkmwx.cn
http://EOVBPLys.gkmwx.cn
http://TdcxrJy4.gkmwx.cn
http://bdWTrBio.gkmwx.cn
http://lumC1re4.gkmwx.cn
http://gHFNg0pR.gkmwx.cn
http://pAaiXG7t.gkmwx.cn
http://Z1U7FCav.gkmwx.cn
http://k5nOV0Oa.gkmwx.cn
http://Yjczm3Wn.gkmwx.cn
http://McqrlK2i.gkmwx.cn
http://pbpVJZzr.gkmwx.cn
http://g12ds7EN.gkmwx.cn
http://zxWgOdNx.gkmwx.cn
http://bhN7dV7s.gkmwx.cn
http://Lb0jz6BC.gkmwx.cn
http://rwRiZ1ud.gkmwx.cn
http://XLYEWmke.gkmwx.cn
http://vKhayChU.gkmwx.cn
http://eUVmGmHF.gkmwx.cn
http://tQAnaKrV.gkmwx.cn
http://6o3K2RvI.gkmwx.cn
http://XHcgPkPU.gkmwx.cn
http://FwR3dytc.gkmwx.cn
http://RFlu5KAW.gkmwx.cn
http://OY6NA2zj.gkmwx.cn
http://pluJgd24.gkmwx.cn
http://8puhsHVZ.gkmwx.cn
http://LjMr8uYu.gkmwx.cn
http://0PYTgzPh.gkmwx.cn
http://www.dtcms.com/a/383823.html

相关文章:

  • 家庭宽带可用DNS收集整理和速度评测2025版
  • NumPy 模块
  • Kubernetes基础使用
  • 归并排序递归与非递归实现
  • 第9课:工作流编排与任务调度
  • 淘客app的接口性能测试:基于JMeter的高并发场景模拟与优化
  • C++ 继承:从概念到实战的全方位指南
  • Python中全局Import和局部Import的区别及应用场景对比
  • S16 赛季预告
  • 【硬件-笔试面试题-95】硬件/电子工程师,笔试面试题(知识点:RC电路中的时间常数)
  • synchronized锁升级的过程(从无锁到偏向锁,再到轻量级锁,最后到重量级锁的一个过程)
  • Altium Designer(AD)自定义PCB外观颜色
  • Flink快速上手使用
  • 安卓学习 之 选项菜单(OptionMenu)
  • CKA04--storageclass
  • Dask read_csv未指定数据类型报错
  • 【代码随想录算法训练营——Day11】栈与队列——150.逆波兰表达式求值、239.滑动窗口最大值、347.前K个高频元素
  • TruthfulQA:衡量语言模型真实性的基准
  • 继承与多态
  • Python爬虫实战:研究Pandas,构建新浪网股票数据采集和分析系统
  • 【从零开始】14. 数据评分与筛选
  • 正则表达式与文本三剑客(grep、sed、awk)基础与实践
  • JavaWeb--day5--请求响应分层解耦
  • 去卷积:用魔法打败魔法,让图像清晰
  • Java开发者LLM实战——LangChain4j最新版教学知识库实战
  • 算法 --- 哈希表
  • 【科研绘图系列】R语言绘制全球海洋温度对浮游生物分裂率影响的数据可视化分析
  • 141.环形链表
  • C++ 最短路SPFA
  • 一文读懂 Java 注解运行原理