Vue3 中 Hook 函数,解锁新技能!
🎈Vue3 带来了 Composition API
,在这其中,Hooks 是其重要组成部分,本文将深入探讨 Vue3 中 Hooks,帮助你在 Vue3 开发中更加得心应手。
什么是 Hook 函数
Hook
翻译过来是 钩子 的意思,其本质上是一组可复用的函数。简单理解来说,你能够在不同的组件中,实现相同的代码逻辑,以达到代码复用、提高维护性的效果。那为何叫'钩子'呢,我的理解是:
它可以通过特定的函数将逻辑 "钩入" 组件中,使得开发者能够 更灵活地构建和管理组件的功能从而提高代码的可读性以及可维护性 等。
细则:
🐼 看完上面的概念,可能还是一头浆糊,这不就是普普通通封装了一个函数然后给我们调用嘛,跟我们平时的函数有啥区别?是,这也是我在学习过程中很疑惑的点,来看看下面的解释吧!
🐼 在开发过程中,我们经常会发现一些可重复利用的代码块,于是我们将其封装成函数以供调用。这类函数包括工具函数,但又不止工具函数,因为我们可能也会封装一些重复的业务逻辑。但在以往,在前端原生开发中,我们所封装的函数,大多数是"无状态"的,不能够建立数据与视图之间的联系。
🐼 那何为"有状态"的函数呢? 这里的"有状态",实际上是指是否含有响应式变量。我们知道,基于 MVC
架构的 React
框架和基于 MVVM
的 Vue
框架都引入了"状态"这一概念。状态是特殊的 JavaScript
变量,它的变化能够引起视图的变化。在这类框架中,如果一个变量的变化不会引起视图变化,则为普通变量,如果一个变量已经被框架注册为状态,那么这个变量的变化就会引起视图变化,我们称之为响应式变量。
总结即:如果一个函数包含了响应式变量,那么它就是一个 Hook
函数。
Hook 实现原理
🐼 看完上面的解释,我想你应该也能有所领悟,上面提到,所谓 Hook
函数是指包含了响应式变量,能够让组件实现代码复用的函数。
🐼 那在 Vue3 中,Hooks 是基于 Composition API 实现的其实也不难理解,因为它需要基于响应式变量嘛!
🐼Hooks 通过 Vue3 中 setup 函数来使用的,setup 函数是 Vue3 组件中的一个新的生命周期函数,它在组件实例被创建之前调用,并且接收两个参数:props 和 context。在 setup 函数中,我们可以定义和返回组件中需要使用的响应式数据、方法、计算属性等,而这些都可以通过 Hooks 来实现。
Hook 简单实现
知道了它是怎么个事?那我们怎么用呢!来和我一起探讨吧!
因为本篇文章更多的是想让大家明白 hook 函数,所以我们这里就举一个特别简单的例子!
业务需求:追踪鼠标位置
在一个夜黑风高的晚上,卷圣优哉游哉地喝着它的咖啡 ☕,此时它的后端小老弟给它发来信息,说,“大哥!不好了!这老板又改需求了!🤯 说需要渲染出用户鼠标的实时位置,而且几乎每个页面都要!!你可能需要加一下班啊!”这时,可能普通程序员已经慌了,但作为一个资深大佬,我们已经很有经验了!
卷圣心想:我就简单地写个 hook 函数去追踪鼠标位置,然后再到不同视图组件里面去调用,再渲染一下不就成了......,卷圣回复说好的,明白了。
业务实现
没用 hook 前:
<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>Mouse position is at: {{ x }}, {{ y }}</template>
这时,如果多个页面都同时需要的话,你复制这段代码跑来跑去,而且,你要知道的是,每个页面中的 onMounted
跟 onUnmounted
都不一定是空的,并且,变量的命名上,都有可能出现重复的情况发生。这在我们后期维护起来,是非常不方便的!那我们来看看帅气的 hook 方法吧!
用 hook 后:
主页面
<script setup>
import { useMouse } from './mouse.js'
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>
在同一目录下新建 mouse.js
文件,在文件内写入以下内容
// 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 }
}
在这里,我们封装了一个 hook 库来实现鼠标追踪,若有其他组件需要使用,我们只需要将其引入,并使用变量即可,并不需要过多考虑变量重名,生命周期钩子代码过多问题!!当然,这里的逻辑比较清晰,当逻辑比较复杂的时候,你可能更能感受到它的强大!
这里介绍一个 VueUse 库,它是 Vue3 生态中最受欢迎的第三方 Hooks 库之一,这里封装了很多 hook 函数,或许你能在其中找到或学习更为复杂的 hook 函数调用!VueUse 中文文档
Hook 的使用场景
- 逻辑复用:当多个组件需要共享相同的逻辑时,我们可以将这些逻辑封装成一个 Hook,然后在需要的组件中导入并使用它。这样可以避免代码重复,提高代码的复用性。
- 逻辑拆分:对于复杂的组件,我们可以使用 Hooks 将组件的逻辑拆分成多个独立的函数,每个函数负责处理一部分逻辑。这样可以使组件的代码更加清晰、易于维护。
- 副作用管理:Hooks 中的函数可以访问组件的响应式数据,并且可以在组件的生命周期中执行副作用操作(如定时器、事件监听等)。通过使用 Hooks,我们可以更好地管理这些副作用操作,确保它们在组件卸载时得到正确的清理。
Vue3 中的 Hook 与 Vue2 中的 mixn
🐼 可能你也感受到了,它有点像我们以前 Vue2 学习的 mixn
!
🐼 我们都知道 Vue3 引入 Composition API
的写法,当我们引入一个 hooks
函数的时候其实就像在 Vue2 中使用一个 mixin
一样, hooks
函数中的 ref
, reactive
就相当于 mixin
中的 data
,同时 hooks
还可以引入一些生命周期函数, watch
等在 mixin
中都有体现。下面简单展示一下 mixin
的写法,不过多讲解
定义
export const mixins = {
data() {
return {
msg: '',
}
},
computed: {},
created() {
console.log('我是mixin中的created生命周期函数')
},
mounted() {
console.log('我是mixin中的mounted生命周期函数')
},
methods: {
clickMe() {
console.log('我是mixin中的点击事件')
},
},
}
组件中使用
export default {
name: 'App',
mixins: [mixins],
components: {},
created() {
console.log('组件调用minxi数据', this.msg)
},
mounted() {
console.log('我是组件的mounted生命周期函数')
},
}
用过 vue2 的 mixin
的都知道,它虽然可以封装一些逻辑,但是它同时也带来了一些问题。
比如你 引入多个 mixin 它们的 data,methods 命名可能会冲突,当 mixin 多了可能会出现维护性问题,另外 mixin 不是一个函数,因此 不能传递参数来改变它的逻辑,具有一定的局限性 等,但这些问题到了 vue3 的 hooks
中则迎刃而解
Hook 优缺点
优点:
- 提高了代码的复用性和可维护性。
- 使组件的逻辑更加清晰、易于理解。
- 更好地管理组件的副作用操作。
缺点:
- 学习曲线较陡峭,需要熟悉新的编程模式和思维方式。
- 对于小型项目或简单组件,使用 Hooks 可能过于复杂。
- 在 Vue 生态中,第三方 Hooks 的质量和兼容性可能存在差异。
Hooks 书写规范
- 命名规范:自定义 Hooks 应该以“use”为前缀,以区分其他函数和变量。例如:
useUserInfo
、useMousePosition
等。同时,命名应清晰明了,准确描述 Hooks 的功能。 - 参数与返回值:自定义 Hooks 应该接收明确的参数,并返回需要在组件中使用的响应式数据、方法、计算属性等。返回的对象应该具有清晰的属性名和结构。
- 副作用管理:如果自定义 Hooks 包含副作用操作(如定时器、事件监听等),应确保在组件卸载时正确清理这些副作用。可以使用
onMounted
、onUnmounted
等生命周期钩子来管理副作用的添加和移除。 - 文档注释:为自定义 Hooks 编写清晰的文档注释是非常重要的,说明其用途、参数、返回值和使用示例。这将有助于其他开发者理解和使用你的自定义 Hooks。
- 类型定义(如果使用 TypeScript):为自定义 Hooks 提供类型定义可以确保更好的类型安全性和编辑器支持。使用 TypeScript 的泛型功能可以增加 Hooks 的灵活性和可复用性。
- 测试:为自定义 Hooks 编写单元测试是确保其正确性和稳定性的重要手段。测试应该覆盖各种使用场景和边界情况。
总结
Vue3
中 Hook
函数的写法还是比较吃香的,通过将响应式变量进行封装处理,以灵活的方式复用代码来实现不同组件的视图变化!通过本篇文章相信大家已经能对 hooks
函数有一定的理解,目前很多开源项目都封装了大量的 hooks
函数,学习它,让我们更优雅地进行开发吧!
参考资源
传送门