定义
Hooks是钩子,系统运行到某一时期时,会调用被注册到该时机的回调函数。
一系列以
use作为开头的方法,它提供了让你可以完全避开class的写法,在函数式组件中完成生命周期、状态管理、逻辑复用等几乎全部组件开发工作的能力。
简化一下:
一系列方法,提供了在函数式组件中完成开发工作的能力。
import { useSlots, useAttrs } from 'vue'; import { useRouter } from 'vue-router';
// 以上这些方法,也是 vue3 中相关的 Hook!
命名规范和指导思想
通常来说,hooks 的命名都是以 use 作为开头,这个规范也包括了那么我们自定义的 hooks。
在 react 官方文档里,对 hooks 的定义和使用提出了 “一个假设、两个只在” 核心指导思想。
一个假设: 假设任何以 「use」 开头并紧跟着一个大写字母的函数就是一个 Hook。
第一个只在: 只在 React 函数组件中调用 Hook,而不在普通函数中调用 Hook。(Eslint 通过判断一个方法是不是大坨峰命名来判断它是否是 React 函数)
第二个只在: 只在最顶层使用 Hook,而不要在循环,条件或嵌套函数中调用 Hook。
以上 “一个假设、两个只在” 总结自 react 官网:
为什么需要Hooks
更好的状态复用
曾经使用的是mixins,但是弊端太多.
弊端一:难以追溯的方法与属性!
试想一下,如果出现这种代码,你是否会怀疑人生:
export default {
mixins: [ a, b, c, d, e, f, g ], // 当然,这只是表示它混入了很多能力
mounted() {
console.log(this.name)
// mmp!这个 this.name 来自于谁?我难道要一个个混入看实现?
}
}
弊端二:覆盖、同名?贵圈真乱!
当我同时想混入 mixin-a.js 和 mixin-b.js 以同时获得它们能力的时候,不幸的事情发生了:
由于这两个 mixin 功能的开发者惺惺相惜,它们都定义了 this.name 作为属性。
这种时候,你会深深怀疑,mixins 究竟是不是一种科学的复用方式。
弊端三:梅开二度?代价很大!
仍然说上面的例子,如果我的需求发生了改变,我需要的不再是一个简单的状态 name,而是分别需要 firstName 和 lastName。
此时 name-mixin.js 混入的能力就会非常尴尬,因为我无法两次 mixins 同一个文件。
当然,也是有解决方案的,如:
// 动态生成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'),
]
}
确实通过动态生成 mixin 完成了能力的复用,但这样一来,无疑更加地增大了程序的复杂性,降低了可读性。
因此,一种新的 “状态逻辑复用” 就变得极为迫切了——它就是 Hooks!
Hook 的状态复用写法:
// 单个name的写法
const { name, setName } = useName();
// 梅开二度的写法
const { name : firstName, setName : setFirstName } = useName();
const { name : secondName, setName : setSecondName } = useName();
相比于 mixins,它们简直太棒了!
- 方法和属性好追溯吗?这可太好了,谁产生的,哪儿来的一目了然。
- 会有重名、覆盖问题吗?完全没有!内部的变量在闭包内,返回的变量支持定义别名。
- 多次使用,没开N度?你看上面的代码块内不就“梅开三度” 了吗?
就冲 “状态逻辑复用” 这个理由,Hooks 就已经香得我口水直流了。
代码组织
项目、模块、页面、功能,如何高效而清晰地组织代码,这一个看似简单的命题就算写几本书也无法完全说清楚。
但一个页面中,N件事情的代码在一个组件内互相纠缠确实是在 Hooks 出现之前非常常见的一种状态。
那么 Hooks 写法在代码组织上究竟能带来怎样的提升呢?
(假设上图中每一种颜色就代码一种高度相关的业务逻辑)
无论是 vue 还是 react, 通过 Hooks 写法都能做到,将“分散在各种声明周期里的代码块”,通过 Hooks 的方式将相关的内容聚合到一起。
这样带来的好处是显而易见的: “高度聚合,可阅读性提升” 。伴随而来的便是 “效率提升,bug变少” 。
按照“物理学”里的理论来说,这种代码组织方式,就算是“熵减”了。
比 class 组件更容易理解
尤其是
this。
在 react 的 class 写法中,随处可见各种各样的 .bind(this)。(甚至官方文档里也有专门的章节描述了“为什么绑定是必要的?”这一问题)
vue 玩家别笑,computed: { a: () => { this } } 里的 this 也是 undefined。
很显然,绑定虽然“必要”,但并不是“优点”,反而是“故障高发”地段。
但在Hooks 写法中,你就完全不必担心 this 的问题了。
因为:
本来无一物,何处惹尘埃。
Hooks 写法直接告别了 this,从“函数”来,到“函数”去。
妈妈再也不用担心我忘记写 bind 了。
友好的渐进式
渐进式的含义是:你可以一点点深入使用。
无论是 vue 还是 react,都只是提供了 Hooks API,并将它们的优劣利弊摆在了那里。并没有通过无法接受的 break change 来强迫你必须使用 Hooks 去改写之前的 class 组件。
你依然可以在项目里一边写 class 组件,一边写 Hooks 组件,在项目的演进和开发过程中,这是一件没有痛感,却悄无声息改变着一切的事情。
但是事情发展的趋势却很明显,越来越多的人加入了 Hooks 和 组合式API 的大军。