In this article, we will learn about hooks and talk about why both vue and react choose it, why we need hooks, and the similarities and differences between vue and react custom Hooks. I hope it will be helpful to everyone!
Initial understanding of Hooks
in vue
The current situation with react
Listen to the author’s definition and summary of Hooks
Understand "Why we need Hooks
"
Carry out some simple Hooks
practice
At the beginning of 2019, react
officially has hooks
capabilities in the 16.8.x
version.
In June 2019, You Yuxi put forward a proposal about vue3 Component API in vue/github-issues
. (The basis of vue hooks)
In the subsequent react
and vue3
related versions, the related hooks
capabilities began to be recognized by more people accept. [Related recommendations: vuejs video tutorial]
In addition, solid.js
, preact
and other frameworks have also begun to choose to joinhooks
Big family.
It is foreseeable that although class Component
and hooks api
are still going hand in hand, in the next few years, hooks
is very likely to replace class Component
and become the real mainstream in the industry.
hooks
? You didn’t understand me when you were young, just like I didn’t understand
hooks
later.
hooks
The literal translation of "hooks" is "hook", it is not just react
, not even just the front end Rather than being a terminology specific to the industry, it is a term that is well known throughout the industry. Usually refers to:
When the system runs to a certain period, the callback function registered for that time will be called.
The more common hooks are: windows
System hooks can monitor various events of the system. The onload
or addEventListener provided by the browser
Can register methods to be called at various times in the browser.
All of the above can be called "hook".
But obviously, under specific topics in specific fields, the word hooks
is given some special meanings.
Before react@16.x
, when we talked about hooks
, we were probably talking about the "life cycle of the component".
But now, hooks
has a whole new meaning.
Taking react
as an example, hooks
is:
A series of hooks starting with
"use"
Methods, they provide you with the ability to completely avoidclass-style writing
and complete almost all component development work such as life cycle, state management, logic reuse, etc. in functional components.
Simplify it:
A series of methods provide the ability to complete development work in functional components.
(Remember this keyword: Functional component)
import { useState, useEffect, useCallback } from 'react'; // 比如以上这几个方法,就是最为典型的 Hooks
And in vue
, hooks## The definition of # may be more vague, let’s summarize:
In the(Keywords:vue
combined API, starting with
"use", a series of provided It provides methods for development capabilities such as component reuse and state management.
Combined API)
import { useSlots, useAttrs } from 'vue'; import { useRouter } from 'vue-router'; // 以上这些方法,也是 vue3 中相关的 Hook!
useSlots,
useAttrs,
useRouter etc.
vue the combined API itself is a key part of "vue hooks", playing a role in
react hooks's life cycle, The core role of state management. (Such as
onMounted,
ref, etc.).
hooks in
vue and
react seem to be Pretty much the same.
"use"?
hooks starts with
use. This specification also includes: Our custom
hooks.
因为(爱情 误)约定。
在 react
官方文档里,对 hooks
的定义和使用提出了 “一个假设、两个只在” 核心指导思想。(播音腔)
一个假设: 假设任何以 「use
」 开头并紧跟着一个大写字母的函数就是一个 Hook
。
第一个只在: 只在 React
函数组件中调用 Hook
,而不在普通函数中调用 Hook
。(Eslint
通过判断一个方法是不是大坨峰命名来判断它是否是 React
函数)
第二个只在: 只在最顶层使用 Hook
,而不要在循环,条件或嵌套函数中调用 Hook。
因为是约定,因而 useXxx
的命名并非强制,你依然可以将你自定义的 hook
命名为 byXxx
或其他方式,但不推荐。
因为约定的力量在于:我们不用细看实现,也能通过命名来了解一个它是什么。
以上 “一个假设、两个只在” 总结自 react
官网:
https://zh-hans.reactjs.org/docs/hooks-rules.html
https://zh-hans.reactjs.org/docs/hooks-faq.html#what-exactly-do-the-lint-rules-enforce
hooks
?3.1 更好的状态复用
怼的就是你,
mixin
!
在 class
组件模式下,状态逻辑的复用是一件困难的事情。
假设有如下需求:
当组件实例创建时,需要创建一个
state
属性:name
,并随机给此name
属性附一个初始值。除此之外,还得提供一个setName
方法。你可以在组件其他地方开销和修改此状态属性。
更重要的是: 这个逻辑要可以复用,在各种业务组件里复用这个逻辑。
在拥有 Hooks
之前,我首先会想到的解决方案一定是 mixin
。
代码如下:(此示例采用 vue2 mixin
写法 )
// 混入文件:name-mixin.js export default { data() { return { name: genRandomName() // 假装它能生成随机的名字 } }, methods: { setName(name) { this.name = name } } }
// 组件:my-component.vue <template> <div>{{ name }}</div> <template> <script> import nameMixin from './name-mixin'; export default { mixins: [nameMixin], // 通过mixins, 你可以直接获得 nameMixin 中所定义的状态、方法、生命周期中的事件等 mounted() { setTimeout(() => { this.setName('Tom') }, 3000) } } <script>
粗略看来,mixins
似乎提供了非常不错的复用能力,但是,react官方文档直接表明:
为什么呢?
因为 mixins
虽然提供了这种状态复用的能力,但它的弊端实在太多了。
弊端一:难以追溯的方法与属性!
试想一下,如果出现这种代码,你是否会怀疑人生:
export default { mixins: [ a, b, c, d, e, f, g ], // 当然,这只是表示它混入了很多能力 mounted() { console.log(this.name) // mmp!这个 this.name 来自于谁?我难道要一个个混入看实现? } }
又或者:
a.js mixins: [b.js] b.js mixins: [c.js] c.js mixins: [d.js] // 你猜猜看, 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
,它们简直太棒了!
就冲 “状态逻辑复用” 这个理由,Hooks
就已经香得我口水直流了。
3.2 代码组织
熵减,宇宙哲学到编码哲学。
项目、模块、页面、功能,如何高效而清晰地组织代码,这一个看似简单的命题就算写几本书也无法完全说清楚。
但一个页面中,N件事情的代码在一个组件内互相纠缠确实是在 Hooks
出现之前非常常见的一种状态。
那么 Hooks
写法在代码组织上究竟能带来怎样的提升呢?
(假设上图中每一种颜色就代码一种高度相关的业务逻辑)
无论是 vue
还是 react
, 通过 Hooks
写法都能做到,将“分散在各种声明周期里的代码块”,通过 Hooks
的方式将相关的内容聚合到一起。
这样带来的好处是显而易见的:“高度聚合,可阅读性提升”。伴随而来的便是 “效率提升,bug变少”。
按照“物理学”里的理论来说,这种代码组织方式,就算是“熵减”了。
3.3 比 <span style="font-size: 16px;">class</span>
组件更容易理解
尤其是
this
。
在 react
的 class
写法中,随处可见各种各样的 .bind(this)
。(甚至官方文档里也有专门的章节描述了“为什么绑定是必要的?”这一问题)
vue
玩家别笑,computed: { a: () => { this } }
里的 this
也是 undefined
。
很显然,绑定虽然“必要”,但并不是“优点”,反而是“故障高发”地段。
但在Hooks
写法中,你就完全不必担心 this
的问题了。
因为:
本来无一物,何处惹尘埃。
Hooks
写法直接告别了 this
,从“函数”来,到“函数”去。
妈妈再也不用担心我忘记写 bind
了。
3.4 友好的渐进式
随风潜入夜,润物细无声。
渐进式的含义是:你可以一点点深入使用。
无论是 vue
还是 react
,都只是提供了 Hooks
API,并将它们的优劣利弊摆在了那里。并没有通过无法接受的 break change
来强迫你必须使用 Hooks
去改写之前的 class
组件。
你依然可以在项目里一边写 class
组件,一边写 Hooks
组件,在项目的演进和开发过程中,这是一件没有痛感,却悄无声息改变着一切的事情。
但是事情发展的趋势却很明显,越来越多的人加入了 Hooks
和 组合式API
的大军。
hooks
?4.1 环境和版本
在 react
项目中, react
的版本需要高于 16.8.0
。
而在 vue
项目中, vue3.x
是最好的选择,但 vue2.6+
配合 @vue/composition-api
,也可以开始享受“组合式API”的快乐。
4.2 react 的 <span style="font-size: 16px;">Hooks</span>
写法
因为 react Hooks 仅支持“函数式”组件,因此需要创建一个函数式组件 my-component.js
。
// my-component.js import { useState, useEffect } from 'React' export default () => { // 通过 useState 可以创建一个 状态属性 和一个赋值方法 const [ name, setName ] = useState('') // 通过 useEffect 可以对副作用进行处理 useEffect(() => { console.log(name) }, [ name ]) // 通过 useMemo 能生成一个依赖 name 的变量 message const message = useMemo(() => { return `hello, my name is ${name}` }, [name]) return <div>{ message }</div> }
细节可参考
react
官方网站:https://react.docschina.org/docs/hooks-intro.html
4.3 vue 的 <span style="font-size: 16px;">Hooks</span>
写法
vue 的 Hooks
写法依赖于 组合式API
,因此本例采用 <script setup>
来写:
<template> <div> {{ message }} </div> </template> <script setup> import { computed, ref } from 'vue' // 定义了一个 ref 对象 const name = ref('') // 定义了一个依赖 name.value 的计算属性 const message = computed(() => { return `hello, my name is ${name.value}` }) </script>
很明显,vue
组合式API里完成 useState
和 useMemo
相关工作的 API
并没有通过 useXxx
来命名,而是遵从了 Vue
一脉相承而来的 ref
和 computed
。
虽然不符合 react Hook
定义的 Hook
约定,但 vue
的 api
不按照 react
的约定好像也并没有什么不妥。
参考网址:https://v3.cn.vuejs.org/api/composition-api.html
hook
除了官方提供的 Hooks Api
, Hooks
的另外一个重要特质,就是可以自己进行“自定义 Hooks” 的定义,从而完成状态逻辑的复用。
开源社区也都有很多不错的基于 Hooks
的封装,比如 ahooks
(ahooks.js.org/zh-CN/),又比如 vueuse
(vueuse.org/)
我还专门写过一篇小文章介绍
vuehook
:
【一库】vueuse:我不许身为vuer,你的工具集只有lodash!。
https://juejin.cn/post/7030395303433863205
那么,我们应该怎么开始撰写 “自定义Hooks” 呢?往下看吧!
5.1 react 玩家看这里
react
官方网站就专门有一个章节讲述“自定义Hook”。(https://react.docschina.org/docs/hooks-custom.html)
这里,我们扔用文章开头那个 useName
的需求为例,希望达到效果:
const { name, setName } = useName(); // 随机生成一个状态属性 name,它有一个随机名作为初始值 // 并且提供了一个可随时更新该值的方法 setName
如果我们要实现上面效果,我们该怎么写代码呢?
import React from 'react'; export const useName = () => { // 这个 useMemo 很关键 const randomName = React.useMemo(() => genRandomName(), []); const [ name, setName ] = React.useState(randomName) return { name, setName } }
忍不住要再次感叹一次,和 mixins
相比,它不仅使用起来更棒,就连定义起来也那么简单。
可能有朋友会好奇,为什么不直接这样写:
const [ name, setName ] = React.useState(genRandomName())
因为这样写是不对的,每次使用该 Hook
的函数组件被渲染一次时,genRandom()
方法就会被执行一次,虽然不影响 name
的值,但存在性能消耗,甚至产生其他 bug
。
为此,我写了一个能复现错误的demo,有兴趣的朋友可以点开验证:https://codesandbox.io/s/long-cherry-kzcbqr
2022-02-03日补充更正:经掘友提醒,可以通过 React.useState(() => randomName()) 传参来避免重复执行,这样就不需要 useMemo 了,感谢!
5.2 vue 玩家看这里
vue3
官网没有关于 自定义Hook
的玩法介绍,但实践起来也并不困难。
目标也定位实现一个 useName
方法:
import { ref } from 'vue'; export const useName = () => { const name = ref(genRandomName()) const setName = (v) => { name.value = v } return { name, setName } }
5.3 <span style="font-size: 16px;">vue</span>
和 <span style="font-size: 16px;">react</span>
自定义 <span style="font-size: 16px;">Hook</span>
的异同
相似点: 总体思路是一致的 都遵照着 "定义状态数据","操作状态数据","隐藏细节" 作为核心思路。
差异点: 组合式API
和 React函数组件
有着本质差异vue3
的组件里, setup
是作为一个早于 “created” 的生命周期存在的,无论如何,在一个组件的渲染过程中只会进入一次。React函数组件
则完全不同,如果没有被 memorized
,它们可能会被不停地触发,不停地进入并执行方法,因此需要开销的心智相比于vue
其实是更多的。
本文转载自:https://juejin.cn/post/7066951709678895141
作者:春哥的梦想是摸鱼
The above is the detailed content of What about hooks, why do both vue and react choose it?. For more information, please follow other related articles on the PHP Chinese website!