ReactHooks的实现必须依赖Fiber么?

作者:zxg_神说要有光

原文链接:https://juejin.cn/post/7087172219226292237

ReactHooks的实现必须依赖Fiber么?

React 的 hooks 是在 fiber 之后出现的特性,所以很多人误以为 hooks 是必须依赖 fiber 才能实现的,其实并不是,它们俩没啥必然联系。

现在,不止 react 中实现了 hooks,在 preact、react ssr、midway 等框架中也实现了这个特性,它们的实现就是不依赖 fiber 的。

我们分别来看一下这些不同框架中的 hooks 都是怎么实现的:

react 如何实现 hooks

react 是通过 jsx 描述界面的,它会被 babel 或 tsc 等编译工具编译成 render function,然后执行产生 vdom:

ReactHooks的实现必须依赖Fiber么?

这里的 render function 在 React17 之前是 React.createElement:

ReactHooks的实现必须依赖Fiber么?

在 React 17 之后换成了 jsx:

ReactHooks的实现必须依赖Fiber么?

这个 jsx-runtime 会自动引入,不用像之前那样每个组件都要保留一个 React 的 import 才行。

render function 执行产生 vdom:

ReactHooks的实现必须依赖Fiber么?

vdom 的结构是这样的:

ReactHooks的实现必须依赖Fiber么?

在 React16 之前,会递归渲染这个 vdom,增删改真实 dom。

ReactHooks的实现必须依赖Fiber么?

而在 React16 引入了 fiber 架构之后就多了一步:首先把 vdom 转成 fiber,之后再渲染 fiber。

ReactHooks的实现必须依赖Fiber么?

vdom 转 fiber 的过程叫做 reconcile,最后增删改真实 dom 的过程叫做 commit。

为什么要做这样的转换呢?

因为 vdom 只有子节点 children 的引用,没有父节点 parent 和其他兄弟节点 sibling 的引用,这导致了要一次性递归把所有 vdom 节点渲染到 dom 才行,不可打断。

万一打断了会怎么样呢?因为没有记录父节点和兄弟节点,那只能继续处理子节点,却不能处理 vdom 的其他部分了。

所以 React 才引入了这种 fiber 的结构,也就是有父节点 return、子节点 child、兄弟节点 sibling 等引用,可以打断,因为断了再恢复也能找到后面所有没处理过的节点。

fiber 节点的结构是这样的:

ReactHooks的实现必须依赖Fiber么?

这个过程可以打断,自然也就可以调度,也就是 schdule 的过程。

所以 fiber 架构就分为了 schdule、reconcile(vdom 转 fiber)、commit(更新到 dom)三个阶段。

函数组件内可以用 hooks 来存取一些值,这些值就是存在 fiber 节点上的。

比如这个函数组件内用到了 6 个 hook:

ReactHooks的实现必须依赖Fiber么?

那么对应的 fiber 节点上就有个 6 个元素的 memorizedState 链表:

ReactHooks的实现必须依赖Fiber么?

通过 next 串联起来:

ReactHooks的实现必须依赖Fiber么?

不同的 hook 在 memorizedState 链表不同的元素上存取值,这就是 react hooks 的原理。

这个链表有创建阶段和更新阶段,所以你会发现 useXxx 的最终实现都分为了 mountXxx 和 updateXxx:

ReactHooks的实现必须依赖Fiber么?

这里的 mount 阶段就是创建 hook 节点并组装成链表的:

ReactHooks的实现必须依赖Fiber么?

会把创建好的 hook 链表挂到 fiber 节点的 memorizedState 属性上。

那更新的时候自然也就能从 fiber 节点上取出这个 hook 链表:

ReactHooks的实现必须依赖Fiber么?

这样在多次渲染中,useXxx 的 api 都能在 fiber 节点上找到对应的 memorizedState。

这就是 react hooks 的原理,可以看到它是把 hook 存在 fiber 节点上的。

那 preact 有什么不同呢?

preact 如何实现 hooks

preact 是兼容 react 代码的更轻量级的框架,它支持 class 组件和 function 组件,也支持了 hooks 等 react 特性。不过它没有实现 fiber 架构。

因为它主要考虑的是体积的极致(只有 3kb),而不是性能的极致。

ReactHooks的实现必须依赖Fiber么?

刚才我们了解了 react 是把 hook 链表存放在 fiber 节点上的,那 preact 没有 fiber 节点,会把 hook 链表存在哪呢?

其实也很容易想到,fiber 只是对 vdom 做了下改造用于提升性能的,和 vdom 没啥本质的区别,那就把 hook 存在 vdom 上不就行了?

确实,preact 就是把 hook 链表放在了 vdom 上。

比如这个有 4 个 hooks 的函数组件:

ReactHooks的实现必须依赖Fiber么?

它的实现就是在 vdom 上存取对应的 hook:

ReactHooks的实现必须依赖Fiber么?

ReactHooks的实现必须依赖Fiber么?

它没有像 react 那样把 hook 分为 mount 和 update 两个阶段,而是合并到一起处理了。

如图,它把 hooks 存在了 component.__hooks 的数组上,通过下标访问。

这个 component 就是 vdom 上的一个属性:

ReactHooks的实现必须依赖Fiber么?

也就是把 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 方法:

ReactHooks的实现必须依赖Fiber么?

ssr 的时候使用 react-dom/server 的 renderToString 方法或 renderToStream 方法:

ReactHooks的实现必须依赖Fiber么?

大家觉得 ssr 的时候会做 vdom 到 fiber 的转换么?

肯定不会呀,fiber 是为了提高在浏览器中运行时的渲染性能,把计算变成可打断的,在空闲时做计算,才引入的一种结构。

服务端渲染自然就不需要 fiber。

不需要 fiber 的话,它把 hook 链表存放在哪里呢?vdom 么?

确实可以放在 vdom,但是其实并没有。

比如 useRef 这个 hooks:

ReactHooks的实现必须依赖Fiber么?

它是从 firstWorkInProgressHook 开始的用 next 串联的一个链表。

ReactHooks的实现必须依赖Fiber么?

而 firstWorkInProgressHook 最开始用 createHook 创建的第一个 hook 节点:

ReactHooks的实现必须依赖Fiber么?

并没有挂载到 vdom 上。

为什么呢?

因为 ssr 只需要渲染一次呀,又不需要更新,自然没必要挂到 vdom 上。

只要每次处理完每个组件的 hooks 就清空一下这个 hook 链表就行:

ReactHooks的实现必须依赖Fiber么?

ReactHooks的实现必须依赖Fiber么?

ReactHooks的实现必须依赖Fiber么?

所以,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 框架:

ReactHooks的实现必须依赖Fiber么?

服务端框架自然就没有 vdom、fiber 这种结构,不过 hooks 的思想并不依赖这些,实现 hooks 的 api 只需要在某个上下文放一个链表就行。

midway 就实现了类似 react hooks 的 api:

ReactHooks的实现必须依赖Fiber么?

ReactHooks的实现必须依赖Fiber么?

具体它这个 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/flyFish.html

Github地址: https://github.com/CloudWise-OpenSource/FlyFish

Gitee地址: https://gitee.com/CloudWise/fly-fish

行业案例:https://www.bilibili.com/video/BV1z44y1n77Y/

部分大屏案例:

ReactHooks的实现必须依赖Fiber么?

Original: https://www.cnblogs.com/cloudwise/p/16170959.html
Author: 云智慧AIOps社区
Title: ReactHooks的实现必须依赖Fiber么?

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/711864/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • JS树形数据操作

    JS树形数据操作 1. 给树形菜单添加一个唯一标识 function renderTreeFunc(data) { let arrs = []; let i = 1; const …

    技术杂谈 2023年6月1日
    095
  • 大数据各组件重要技术点总结

    针对大数据组件特点归纳如下: 存储:HDFS,hudi,Hbase, Kafka 计算引擎:Spark,Flink OLAP: Doris 调度: Yarn 下面主要从架构、组件原…

    技术杂谈 2023年7月11日
    065
  • python 对潜在客户数据集 进行数据分析

    大家好,我是小寒。 今天给大家带来一篇 探索性数据分析(EDA) 案例分享。如果觉得不错,可以多多分享。 什么是探索性数据分析 探索性数据分析 (EDA) 是任何数据科学或数据分析…

    技术杂谈 2023年7月25日
    081
  • Java项目有可能做到所有的代码逻辑均可热部署吗?

    前言 首先我们明确下什么叫做热部署,热部署是在不重启java虚拟机的前提下,自动更新class的行为,从而更新整个运行时的逻辑。 在java开发领域,热部署一直是一个难以解决的问题…

    技术杂谈 2023年7月11日
    071
  • Seata1.5.2源码学习

    文章有点长,我决定用半个小时来和你分享~😂 废话不多说,上代码。。。 基于Seata 1.5.2,项目中用 seata-spring-boot-starter SeataDataS…

    技术杂谈 2023年7月24日
    0100
  • 双缓冲绘图

    双缓冲绘图 大家小时候都玩过飞机大战吧,当我们在玩这种飞行射击类游戏时,背景图总是不断地向下移动的,从而给我们营造出一种飞机正在向前飞行的游戏体验。那么,图片的快速变化是如何实现的…

    技术杂谈 2023年7月23日
    088
  • 编程思想与算法leetcode_分治算法详解

    一、基本概念 在计算机科学中,分治法是一种很重要的算法。字面上的解释是”分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更…

    技术杂谈 2023年7月25日
    073
  • 使用Gulp和Browserify来搭建React应用程序

    对React有一定了解之后,我们知道,需要把JSX文件转换成JS文件,组件需要导入导出。本篇就体验使用Gulp把JSX文件转换成JS文件,使用Browserify来把组件捆绑到一个…

    技术杂谈 2023年5月31日
    0133
  • 虚拟环境搭建

    虚拟环境搭建 我们进行开发的时候虚拟环境搭建尤为重要,我们如果需要的python解释器模块版本不一样可以采用这个办法 pycharm中搭建 命令创建虚拟环境 比如centos没有图…

    技术杂谈 2023年6月21日
    098
  • 阿里云Linux-Centos8安装mysql8

    1. 安装MySQL 依次执行以下&#x…

    技术杂谈 2023年6月21日
    087
  • xss 防范

    https://www.npmjs.com/package/xss xss的案例: https://portswigger.net/web-security/cross-site-…

    技术杂谈 2023年5月31日
    088
  • Pillow 图片处理模块

    pic = Image.open(’11.jpg’) im = Image.new(‘RGB’, (128, 128), (255, 0, 0)) im1 = Image.new(…

    技术杂谈 2023年6月21日
    099
  • Windows安装mysql数据库

    一般我安装mysql用以下两个方法: 一.phpstudy环境下的mysql安装 只需将mysql的bin目录配置到系统环境变量即可, 输入默认密码root即可登录 二.本地直接安…

    技术杂谈 2023年7月11日
    080
  • crontab定时任务

    (1)任务配置格式 crontab任务配置基本格式: * *  *  *  * &#…

    技术杂谈 2023年7月24日
    071
  • dotenv 加载本地环境变量

    https://www.npmjs.com/package/dotenv Original: https://www.cnblogs.com/mengfangui/p/157928…

    技术杂谈 2023年5月31日
    073
  • 有点长的博客:Redis不是只有get set那么简单

    我以前还没接触Redis的时候,听到大数据组的小伙伴在讨论Redis,觉得这东西好高端,要是哪天我们组也可以使用下Redis就好了,好长一段时间后,我们项目中终于引入了Redis这…

    技术杂谈 2023年7月25日
    0103
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球