hooks 大势所趋
2019年年初,react 在 16.8.x 版本正式具备了 hooks 能力,同年6月;尤雨溪在 vue/github-issues 里提出了关于 vue3 Component API 的提案(vue hooks的基础)。在Vue3的组合式API出现后,github的一系列基于hooks的项目也是异常火爆,比较突出的是VueUse(16K stars)和 ahooks(12K stars)。由此可以预见hooks将成为业界主流,并深受广大前端程序员喜爱。
什么是hooks?
“hooks” 直译是 “钩子”,指系统运行到某一时期时,会调用被注册到该时机的回调函数。在前端提到钩子可能会跟组件的生命周期函数联系起来,但是现在明显已经给hooks赋予了新的含义。在Vue中hooks的定义为:在 vue 组合式API里,以 “use” 作为开头的,一系列提供了组件复用、状态管理等开发能力的方法。
我们为什么需要hooks?
- 1.hooks对比于Vue2的mixin,解决了mixin的难以追朔的方法和属性问题
mixin例子 数据来源无法追朔
export default {
mixins: [ a, b, c, d, e, f, g ]
mounted() {
console.log(this.name) // this.name 来自于谁?
}
}
hooks例子 数据方法来源清晰可见
<script setup lang="ts">
const { name, setName } = useName()
</script>
- 2.解决了mixin很难在一个页面重复使用(实例化多次)
mixin实现多实例繁琐且代码不易读
// 动态生成mixin
function genNameMixin(key, funcKey) {
return {
data() {
return {
[key]: genRandomName()
}
},
methods: {
[funcKey]: function(v) {
this.[key] = v
}
}
}
}
export default {
mixins: [
genNameMixin('firstName', 'setFirstName'),
genNameMixin('lastName', 'setLastName'),
]
}
hooks 实现多实例 简单明了
- 3.hooks+组合式API优雅地实现代码组织
假设把左图中分散在各处的data、mounted、methods,通过hooks按照业务组合在一起(如右图,也就是hooks和组合式API的完美结合),形成”高内聚低耦合“的代码结构,随之而来的则是代码复用率变高、效率提升、可阅读性变强、bug变少。
Vue3中如何使用hooks
- 命名规范和指导思想
- 约定1: 一般任何以 「use」 开头并紧跟着一个大写字母的函数就是一个 Hook
- 约定2:一般只在最顶层使用 Hook,而不要在循环,条件或嵌套函数中调用 Hook
- 下面是一个简单的hooks例子,下面将用这个例子进行延展
定义了一个响应式数据name和修改name的函数setName
import { ref } from 'vue'
export default function useName() {
const name = ref('张三')
const setName = (val: string) => {
name.value = val
}
return { name, setName }
}
- 疑问1:hooks函数返回数组行不行?
import { ref } from 'vue'
export default function useName() {
const name = ref('张三')
const setName = (val: string) => {
name.value = val
}
return [ name, setName ] // 返回数组
}
答:hooks返回数组当然可以,要分情况,如果hooks在一个页面内需要重复使用(也可以理解为实例化多次)则返回数组更为合适,因为我们更灵活的使用别名,demo如下:
// 使用useName
import useName from './useName'
const [name,setName] = useName()
const [firstName,setFirstName] = useName()
const [secondName,setSecondName] = useName()
反之,返回对象的hooks多实例情况如下,当然除了写法更加麻烦没其他的区别。
// 使用useName
import useName from './useName'
const {name,setName} = useName()
const {name:firstName,setName:setFirstName} = useName()
const {name:secondName,setName:setSecondName} = useName()
- 疑问2:hooks 跟普通object写法有何区别?如下
用对象模拟hooks函数
import { ref } from 'vue'
export const useName = {
name: ref('张三'),
setName: (val: string) => {
useObj.name.value = val
}
}
答:粗略看区别不大,都能定义响应式数据,也都返回对象{name,setName}。但本质上是有区别的,该写法无法实现多实例需求,也就是说它是单例的,两个组件同时引用name数据,一个修改了则全部修改(这是区别,不是弊端,有数据/状态共享需求可以使用该方法)。
- 疑问3: hooks是否可以使用全部组合式API?比如onUnmounted
答:这个官方案例已经给答案了,全部组合式API都可以使用包括onUnmounted,在组件销毁时也会触发
<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>
-
疑问4:hooks里写业务行不行?即使它不被复用,单纯用它解耦
答:这个问题仁者见仁智者见智,作者认为使用hooks不用有太多思想包袱,如果需求是高复用工具函数,封装成hooks自然无可厚非,但是单纯用它处理业务闭环或处理业务逻辑拆分也是没问题的,而且这也是hooks的主要用途没有之一,它是彻底告别”屎山“业务代码的利器。 -
疑问5:用hooks做数据/状态共享是否可行?
答:肯定是不行的,上面问题提到hooks在调用时会生成新的实例,调用hooks返回的状态/数据内存地址是不同的,因此无法做到单例和数据共享。
最后;如果您有更多关于hooks的疑问,欢迎留言交流