共计 4134 个字符,预计需要花费 11 分钟才能阅读完成。
作者:zxg_神说要有光
原文链接:https://juejin.cn/post/708717…
React 的 hooks 是在 fiber 之后呈现的个性,所以很多人误以为 hooks 是必须依赖 fiber 能力实现的,其实并不是,它们俩没啥必然联系。
当初,不止 react 中实现了 hooks,在 preact、react ssr、midway 等框架中也实现了这个个性,它们的实现就是不依赖 fiber 的。
咱们别离来看一下这些不同框架中的 hooks 都是怎么实现的:
react 如何实现 hooks
react 是通过 jsx 形容界面的,它会被 babel 或 tsc 等编译工具编译成 render function,而后执行产生 vdom:
这里的 render function 在 React17 之前是 React.createElement:
在 React 17 之后换成了 jsx:
这个 jsx-runtime 会主动引入,不必像之前那样每个组件都要保留一个 React 的 import 才行。
render function 执行产生 vdom:
vdom 的构造是这样的:
在 React16 之前,会递归渲染这个 vdom,增删改实在 dom。
而在 React16 引入了 fiber 架构之后就多了一步:首先把 vdom 转成 fiber,之后再渲染 fiber。
vdom 转 fiber 的过程叫做 reconcile,最初增删改实在 dom 的过程叫做 commit。
为什么要做这样的转换呢?
因为 vdom 只有子节点 children 的援用,没有父节点 parent 和其余兄弟节点 sibling 的援用,这导致了要一次性递归把所有 vdom 节点渲染到 dom 才行,不可打断。
万一打断了会怎么样呢?因为没有记录父节点和兄弟节点,那只能持续解决子节点,却不能解决 vdom 的其余局部了。
所以 React 才引入了这种 fiber 的构造,也就是有父节点 return、子节点 child、兄弟节点 sibling 等援用,能够打断,因为断了再复原也能找到前面所有没解决过的节点。
fiber 节点的构造是这样的:
这个过程能够打断,天然也就能够调度,也就是 schdule 的过程。
所以 fiber 架构就分为了 schdule、reconcile(vdom 转 fiber)、commit(更新到 dom)三个阶段。
函数组件内能够用 hooks 来存取一些值,这些值就是存在 fiber 节点上的。
比方这个函数组件内用到了 6 个 hook:
那么对应的 fiber 节点上就有个 6 个元素的 memorizedState 链表:
通过 next 串联起来:
不同的 hook 在 memorizedState 链表不同的元素上存取值,这就是 react hooks 的原理。
这个链表有创立阶段和更新阶段,所以你会发现 useXxx 的最终实现都分为了 mountXxx 和 updateXxx:
这里的 mount 阶段就是创立 hook 节点并组装成链表的:
会把创立好的 hook 链表挂到 fiber 节点的 memorizedState 属性上。
那更新的时候天然也就能从 fiber 节点上取出这个 hook 链表:
这样在屡次渲染中,useXxx 的 api 都能在 fiber 节点上找到对应的 memorizedState。
这就是 react hooks 的原理,能够看到它是把 hook 存在 fiber 节点上的。
那 preact 有什么不同呢?
preact 如何实现 hooks
preact 是兼容 react 代码的更轻量级的框架,它反对 class 组件和 function 组件,也反对了 hooks 等 react 个性。不过它没有实现 fiber 架构。
因为它次要思考的是体积的极致(只有 3kb),而不是性能的极致。
方才咱们理解了 react 是把 hook 链表寄存在 fiber 节点上的,那 preact 没有 fiber 节点,会把 hook 链表存在哪呢?
其实也很容易想到,fiber 只是对 vdom 做了下革新用于晋升性能的,和 vdom 没啥实质的区别,那就把 hook 存在 vdom 上不就行了?
的确,preact 就是把 hook 链表放在了 vdom 上。
比方这个有 4 个 hooks 的函数组件:
它的实现就是在 vdom 上存取对应的 hook:
它没有像 react 那样把 hook 分为 mount 和 update 两个阶段,而是合并到一起解决了。
如图,它把 hooks 存在了 component.__hooks 的数组上,通过下标拜访。
这个 component 就是 vdom 上的一个属性:
也就是把 hooks 的值存在了 vnode._component._hooks 的数组上。
比照下 react 和 preact 实现 hooks 的差别:
- react 中是把 hook 链表寄存在 fiberNode.memorizedState 属性上,preact 中是把 hook 链表寄存在 vnode._component._hooks 属性上
<!—->
- react 中的 hook 链表通过 next 串联,preact 中的 hook 链表就是个数组,通过下标拜访
<!—->
- react 把 hook 链表的创立和更新分来到,也就是 useXxx 会分为 mountXxx 和 updateXxx 来实现,而 preact 中合并在一起解决的
所以说,hooks 的实现并不依赖 fiber,它只不过是找个中央寄存组件对应的 hook 的数据,渲染时能取到就行,寄存在哪里是无所谓的。
因为 vdom、fiber 和组件渲染强相干,所以寄存在了这些构造上。
像 react ssr 实现 hooks,就既没有存在 fiber 上,也没有存在 vdom 上:
react ssr 如何实现 hooks
其实 react-dom 包除了能够做 csr 外,也能够做 ssr:
csr 时应用 react-dom 的 render 办法:
ssr 的时候应用 react-dom/server 的 renderToString 办法或 renderToStream 办法:
大家感觉 ssr 的时候会做 vdom 到 fiber 的转换么?
必定不会呀,fiber 是为了进步在浏览器中运行时的渲染性能,把计算变成可打断的,在闲暇时做计算,才引入的一种构造。
服务端渲染天然就不须要 fiber。
不须要 fiber 的话,它把 hook 链表寄存在哪里呢?vdom 么?
的确能够放在 vdom,然而其实并没有。
比方 useRef 这个 hooks:
它是从 firstWorkInProgressHook 开始的用 next 串联的一个链表。
而 firstWorkInProgressHook 最开始用 createHook 创立的第一个 hook 节点:
并没有挂载到 vdom 上。
为什么呢?
因为 ssr 只须要渲染一次呀,又不须要更新,天然没必要挂到 vdom 上。
只有每次解决完每个组件的 hooks 就清空一下这个 hook 链表就行:
所以,react ssr 时,hooks 是存在全局变量上的。
比照下 react csr 和 ssr 时的 hooks 实现原理的区别:
- csr 时会从 vdom 创立 fiber,用于把渲染变成可打断的,通过闲暇调度来进步性能,而 ssr 时不会,是 vdom 间接渲染的
<!—->
- csr 时把 hooks 保留到了 fiber 节点上,ssr 时是间接放在了全局变量上,每个组件解决完就清空。因为不会用第二次了
<!—->
- csr 时会把 hook 的创立和更新分为 mount 和 update 两个阶段,而 ssr 因为只会解决一次,只有创立阶段
hooks 的实现原理其实不简单,就是在某个上下文中寄存一个链表,而后 hooks api 从链表不同的元素上拜访对应的数据来实现各自的逻辑。这个上下文能够是 vdom、fiber 甚至是全局变量。
不过 hooks 这个思维还是挺火的,淘宝出的服务端框架 midway 就在引入了 hooks 的思维:
midway 如何实现 hooks
midway 是一个 Node.js 框架:
服务端框架天然就没有 vdom、fiber 这种构造,不过 hooks 的思维并不依赖这些,实现 hooks 的 api 只须要在某个上下文放一个链表就行。
midway 就实现了相似 react hooks 的 api:
具体它这个 hook 链表存在哪我还没看,不过咱们曾经把握 hooks 的实现原理了,只有有个上下文寄存 hook 链表就行,在哪都能够。
总结
react hooks 是在 react fiber 架构之后呈现的个性,很多人误以为 hooks 必须配合 fiber 能力实现,咱们别离看了 react、preact、react ssr、midway 中的 hooks 的实现,发现并不是这样的:
- react 是把 vdom 转成 fiber,而后把 hook 链表寄存到了 fiber.memorizedState 属性上,通过 next 串联
<!—->
- preact 没有实现 fiber,它是把 hook 链表放到了 vnode._component._hooks 属性上,数组实现的,通过下标拜访
<!—->
- react ssr 时不须要 fiber,然而也没有把 hook 链表挂到 vdom 上,而是间接放在了一个全局变量上,因为只须要渲染一次,渲染完一个组件就清空这个全局变量就行
<!—->
- midway 是一个 Node.js 框架,它也实现了 hooks 相似的 api,具体放在哪咱们没深刻,然而只有有个上下文寄存 hook 链表就行
所以,react hooks 必须依赖 fiber 能力实现么?
显著不是,搭配 fiber、搭配 vdom、搭配全局变量,甚至任何一个上下文都能够。
写在最初
近年来,在 AIOps 畛域疾速倒退的背景下,IT 工具、平台能力、解决方案、AI 场景及可用数据集的迫切需要在各行业爆发。基于此,云智慧在 2021 年 8 月公布了 AIOps 社区, 旨在树起一面开源旗号,为各行业客户、用户、研究者和开发者们构建沉闷的用户及开发者社区,独特奉献及解决行业难题、促成该畛域技术倒退。
社区先后 开源 了数据可视化编排平台 -FlyFish、运维治理平台 OMP 、云服务治理平台 - 摩尔平台、 Hours 算法等产品。
可视化编排平台 -FlyFish:
我的项目介绍:https://www.cloudwise.ai/flyF…
Github 地址:https://github.com/CloudWise-…
Gitee 地址:https://gitee.com/CloudWise/f…
行业案例:https://www.bilibili.com/vide…
局部大屏案例: