下一个泡泡玛特工程师是谁呢?

收集一些自己用到的prompt

2023-04-05 · 1 min read

机械键盘相关参数

机械键盘相关参数记录

2023-04-05 · 1 min read

Web 前端性能优化

常用的web前端性能优化手段

2023-03-19 · 3 min read

Blender常用快捷键

自己经常用到的一些快捷键记录

2022-12-11 · 1 min read

Docker 常用命令

精简 Docker 常用命令

2022-11-06 · 1 min read

项目管理

项目管理相关知识

2022-11-06 · 1 min read

JS沙箱sandbox的各种实现

我们把Js隔离机制常常称作沙箱

2022-09-18 · 1 min read

Amazing!Solid 比react还react

今天来介绍2个amazing的东东

2022-09-12 · 1 min read

puppeteer应用

使用傀儡师来操作浏览器这个傀儡吧

2022-09-11 · 1 min read

做一个web termianl

前端react, 后端nodejs, 直接可用版web termianl

2022-07-17 · 1 min read

TypeScript里常用的工具类型

经常用到的工具类型,提取抽离出来,供以后复用

2022-06-26 · 1 min read

Vim大法好

想要丢掉鼠标,试试Vim

2022-06-26 · 1 min read

碧血丹心

无论时光如何沾染风霜,也永似红日光

2022-05-10 · 1 min read

chrome V8 引擎中的垃圾回收机制

V8引擎就是nodejs的发动机

2022-03-03 · 1 min read

如何开发一个cli

命令行交互界面是程序员必备的工具,如何开发一个呢?

2022-02-21 · 1 min read

Javascript中的哲学

道生一,一生二,二生三,三生万物

2022-02-20 · 1 min read

React Redux 实现 (Context 版)

React Redux 实现 (Context 版)

2022-02-16 · 1 min read

mobx-react 使用

虽然不常用,但是可以学一下

2022-02-16 · 1 min read

Less中的for和forEach循环

循环的使用是保持代码干燥和避免重复的好方法

2022-02-15 · 1 min read

Webpack Splitchunks 详解

webpack 优化

2022-02-15 · 1 min read

ssh-keygen命令详解

为ssh生成、管理和转换认证密钥

2022-02-15 · 1 min read

使用CURL发送POST请求

curl 是常用的命令行工具,用来请求 Web 服务器。

2022-02-15 · 1 min read

Webpack Plugin 开发

让我们来学一下如何开发一个webpack插件

2022-02-14 · 1 min read

JS 实现两个大数相加?

algo-adding-large-numbers

2022-02-10 · 1 min read

字典树 trie

字典树 trie

2022-02-09 · 1 min read

浏览器原理问题

浏览器原理问题

2022-01-11 · 1 min read

MacBook快速进入一个文件夹目录

mac如何快速进入一个文件夹

2021-10-26 · 1 min read

react合成事件

react-synthetic-event

2021-10-25 · 1 min read

mini webpack实现

通过babel核心来实现迷你版的webpack

2021-10-10 · 1 min read

设计模式

在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于类

2021-10-10 · 1 min read

babel核心

babel核心介绍

2021-10-07 · 1 min read

React 15 和 React 16 的区别

react-15-16

2021-10-06 · 1 min read

React性能优化

浅谈react性能优化的方法

2021-10-05 · 1 min read

交通信号灯实现

如何用js来实现交通信号灯呢

2021-09-25 · 1 min read

内存管理

前端中的内存管理

2021-09-25 · 1 min read

前端安全

前端关于安全方面的知识

2021-09-25 · 2 min read

网络和并发

http各版本对于并发的支持,前端如何控制并发量?

2021-09-25 · 1 min read

跨域方法

总结了9种跨域方法

2021-09-24 · 1 min read

react virtualList 虚拟列表无限滚动实现

用react实现虚拟滚动

2021-09-16 · 1 min read

监控埋点方案

前端监控埋点方案

2021-09-16 · 1 min read

Mini useEffect实现

如何实现useEffect?

2021-09-11 · 1 min read

Mini useState 实现

我们来思考一下useState是怎么实现的呢?

2021-09-11 · 1 min read

React Fiber

react-fiber

2021-09-11 · 1 min read

React class组件和function组件异同

类组件和函数组件有何相同点有何不同点呢?

2021-09-11 · 1 min read

Lodash Get 实现

algo-lodash-get

2021-09-08 · 1 min read

手写reduce实现

algo-reduce

2021-09-08 · 1 min read

Hooks 原理概览

react-hooks

2021-09-05 · 1 min read

Hook原理——状态Hook

react-hook-state

2021-09-05 · 1 min read

手写Mini Redux实现

手写一个简易版的redux实现,包含了核心逻辑

2021-09-04 · 1 min read

legacy和concurrent模式

react-legacy-concurrent

2021-09-01 · 1 min read

react架构

总体 react 的核心可以用 ui=fn(state)来表示 3 大核心对象、3 大核心阶段、2 大工作循环 Scheduler(调度器): 排序优先级,让优先级高的任务先进行 reconcile Reconciler…

2021-09-01 · 2 min read

react核心api和jsx

为什么要有jsx,为什么会有虚拟bom

2021-09-01 · 1 min read

setState是同步的还是异步的

react-setstate-usestate

2021-08-31 · 1 min read

如何使用NodeJs创建HTTP服务?

如何使用NodeJs创建HTTP服务?

2021-08-17 · 1 min read

NodeJS 事件循环模型

nodejs-eventloop

2021-08-11 · 2 min read

Buffer

nodejs中的内存管理

2021-08-10 · 1 min read

微前端解决方案-qiankun

目前国内最好的微前端解决方案-qiankun

2021-08-10 · 1 min read

React Mini版实现(1)

学一门技术最好的方法就是做一个其玩具版的实现,我们来尝试实现一下react和react-dom最简单版本吧

2021-08-04 · 1 min read

brew安装

brew 是 MacOS 上的包管理工具,可以简化 macOS 和 Linux 操作系统上软件的安装。

2021-08-04 · 1 min read

CommonJS简易版实现

CommonJS我们经常用,如何实现一个简易版的commonJS呢?

2021-08-01 · 1 min read

极品透明Dashboard样式分享

一个极品透明Dashboard样式分享

2021-07-30 · 1 min read

Stream

nodejs中的流

2021-07-11 · 1 min read

NodeJS全局对象

JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

2021-05-18 · 1 min read

如何部署Nodejs服务

如何快速的部署一个Nodejs服务到公网呢?

2021-05-12 · 1 min read

Events

events模块是node的核心模块之一,几乎所有常用的node模块都继承了events模块,比如http、fs等。

2021-05-11 · 1 min read

用JS绘制背景,让CSS直接使用 'background:paint(xxx)'

CSS对象新特性,新的background设置方式

2021-04-30 · 1 min read

Block Formatting Context 块级格式化上下文

可以将BFC看成是元素的一种属性,拥有了这种属性的元素就会使他的子元素与世隔绝,不会影响到外部其他元素

2021-04-17 · 1 min read

如何使用Nodejs来创建一个TCP/UDP服务?

如何使用Nodejs来创建一个TCP/UDP服务?

2021-04-17 · 1 min read

Mac使用tree生成目录结构

程序员经常会有需求,需要列出项目的结构树。Mac或者Linux下可以使用tree列出项目结构

2021-04-04 · 1 min read

常见算法

一些比较常见算法

2021-03-20 · 1 min read

前端缓存

对于性能优化离不开缓存

2021-02-28 · 1 min read

前端答疑

一些比较常见的问题

2020-09-02 · 1 min read

Nodejs 网络 & HTTP

nodejs-network

2020-08-14 · 1 min read

JavaScript AST 抽象语法树

源代码的抽象语法结构的树状表现形式

2020-08-02 · 1 min read

nodejs里面向切面编程的一种范式

在一些场景下我们可能需要一种面向切面的编程方式

2020-08-01 · 1 min read

各种JS模块化特性

AMD、CMD、CJS、ESM

2020-05-30 · 1 min read

手写Ajax实现

使用HMR一步步实现Ajax

2020-05-30 · min read

手写PromiseA+实现

如何自己实现promiseA+规范,手写一个promise实现

2020-04-30 · 1 min read

debug和内存泄露

nodejs的debug方法

2020-04-08 · 1 min read

Javascript prototype 原型链

js-prototype

2019-09-07 · 1 min read

this指针、作用域

this是在执行时动态读取上下文决定的,不是在定义时决定

2019-06-14 · 1 min read

call、apply、bind的极简实现

使用symbol实

2019-06-03 · 1 min read

CSS联合选择器区分列表元素个数不同所要求的不同样式

对于列表,在有些时候针对于不同个数的item会有不同的显示,比如col份数,用js固然可以,是否可以用css更简便的实现呢?

2019-04-30 · 1 min read

TypeScript基础

介绍TypeScript基础知识

2019-04-30 · 1 min read

JS中的变量提升

为什么js当时要这样设计

2019-03-07 · 1 min read

ES6之Class

关于ES6里的class, 我们有什么不知道的事?

2019-03-01 · 1 min read

HTTP详解

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议

2019-02-17 · 2 min read

极简代码实现节流Throttle和防抖Debounce

使用各9行代码实现节流和防抖函数

2019-01-30 · 1 min read

Javascript 事件循环 EventLoop

js-eventloop

2018-09-07 · 1 min read

POST和GET区别

POST和GET区别是什么呢?

2018-08-14 · 1 min read

正则表达式

正则表达式一锅端

2018-07-30 · 1 min read

cloneDeep 深克隆实现

algo-clonedeep

2017-09-07 · 1 min read

webpack

engin-webpack

2017-09-07 · 1 min read

http1.1和http2.0有什么区别

http1.1和http2.0有什么区别

2017-06-14 · 1 min read

手写EventEmitter事件巴士

咱们来手写实现一个EventEmitter事件巴士

2017-01-10 · 1 min read

OOP 面向对象编程

对象是什么?为什么要面向对象?

2016-05-08 · 1 min read
Stay hungry & Stay foolish
战歌
16
The Reluctant Warrior
Immediate Music
To Glory
Two Steps From Hell
Victory
Two Steps From Hell
Empire of Angels
Thomas Bergersen
Serenata Immortale
Immediate Music
Cornfield Chase
Hans Zimmer
Tennessee
Hans Zimmer
He's a Pirate
Martin Ermen
Rise
Hans Zimmer
On Thin Ice
Hans Zimmer
Angels Will Rise
Twisted Jukebo
When It All Falls Down
Audiomachine
Icarus
Ivan Torrent
Star Sky - Instrumental
Two Steps From Hell
亡灵序曲
L
Up Is Down
Hans Zimm
回到首页

react架构

袁官东
September 1st, 2021 · 2 min read
图:Mako Tsereteli

总体

react 的核心可以用 ui=fn(state)来表示

1const state = reconcile(update);
2const UI = commit(state);

3 大核心对象、3 大核心阶段、2 大工作循环

  • Scheduler(调度器): 排序优先级,让优先级高的任务先进行 reconcile
  • Reconciler(协调器): 找出哪些节点发生了改变,并打上不同的 Flags(旧版本 react 叫 Tag)
  • Renderer(渲染器): 将 Reconciler 中打好标签的节点渲染到视图上
react1

Scheduler(调度器)

我们知道了要实现异步可中断的更新,需要浏览器指定一个时间,如果没有时间剩余了就需要暂停任务,requestIdleCallback 貌似是个不错的选择,但是它存在兼容和触发不稳定的原因,react17 中采用 MessageChannel 来实现。

在 Scheduler 中的每的每个任务的优先级使用过期时间表示的,如果一个任务的过期时间离现在很近, 说明它马上就要过期了,优先级很高,如果过期时间很长,那它的优先级就低,没有过期的任务存放在 timerQueue 中, 过期的任务存放在 taskQueue 中,timerQueue 和 timerQueue 都是小顶堆, 所以 peek 取出来的都是离现在时间最近也就是优先级最高的那个任务,然后优先执行它。

优先级

1var expirationTime = startTime + timeout;
expirtime

Lane 模型

react 之前的版本用 expirationTime 属性代表优先级,该优先级和 IO 不能很好的搭配工作(io 的优先级高于 cpu 的优先级),现在有了更加细粒度的优先级表示方法 Lane,Lane 用二进制位表示优先级,二进制中的 1 表示位置,同一个二进制数可以有多个相同优先级的位,这就可以表示‘批’的概念,而且二进制方便计算。

这好比赛车比赛,在比赛开始的时候会分配一个赛道,比赛开始之后大家都会抢内圈的赛道(react 中就是抢优先级高的 Lane),比赛的尾声,最后一名赛车如果落后了很多,它也会跑到内圈的赛道,最后到达目的地(对应 react 中就是饥饿问题,低优先级的任务如果被高优先级的任务一直打断,到了它的过期时间,它也会变成高优先级)

Lane 的二进制位如下,1 的 bits 越多,优先级越低

1//ReactFiberLane.js
2export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
3export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
4
5export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
6export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010;
7
8export const InputDiscreteHydrationLane: Lane = /* */ 0b0000000000000000000000000000100;
9const InputDiscreteLanes: Lanes = /* */ 0b0000000000000000000000000011000;
10
11const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000100000;
12const InputContinuousLanes: Lanes = /* */ 0b0000000000000000000000011000000;
13
14export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000100000000;
15export const DefaultLanes: Lanes = /* */ 0b0000000000000000000111000000000;
16
17const TransitionHydrationLane: Lane = /* */ 0b0000000000000000001000000000000;
18const TransitionLanes: Lanes = /* */ 0b0000000001111111110000000000000;
19
20const RetryLanes: Lanes = /* */ 0b0000011110000000000000000000000;
21
22export const SomeRetryLane: Lanes = /* */ 0b0000010000000000000000000000000;
23
24export const SelectiveHydrationLane: Lane = /* */ 0b0000100000000000000000000000000;
25
26const NonIdleLanes = /* */ 0b0000111111111111111111111111111;
27
28export const IdleHydrationLane: Lane = /* */ 0b0001000000000000000000000000000;
29const IdleLanes: Lanes = /* */ 0b0110000000000000000000000000000;
30
31export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;

Reconciler(协调器)

Fiber 双缓存

Fiber 对象上面保存了包括这个节点的属性、类型、dom 等,Fiber 通过 child、sibling、return(指向父节点)来形成 Fiber 树

双缓存是指存在两颗 Fiber 树,current Fiber 树描述了当前呈现的 dom 树,workInProgress Fiber 是正在更新的 Fiber 树,这两颗 Fiber 树都是在内存中运行的,在 workInProgress Fiber 构建完成之后会将它作为 current Fiber 应用到 dom 上

Fiber 本质是环形链表结构,因为虚拟 dom 的结构还是树,所以一般称为 Fiber 树,遍历方式是深度优先遍历,child->sibling->return, child 和 sibling 都执行 beginWork, return 都执行 completeWork,

fiber

遍历到每个节点,都会计算 state, 对比节点差异,为节点赋值相应的 effectFlags。

‘捕获’阶段发生在 beginWork 函数中,该函数做的主要工作是

  • 创建 Fiber 节点,
  • 计算 state
  • diff 算法

‘冒泡’阶段发生在 completeWork 中, 该函数主要是做一些收尾工作,例如

  • 处理节点的 props
  • 形成一条 effectList 的链表

该链表是被标记了更新的节点形成的链表,commit 阶段其实是拿这个 fiberEffectList 做更新

Reconciler 会创建或者更新 Fiber 节点。在 mount 的时候会根据 jsx 生成 Fiber 对象, 在 update 的时候会根据最新的 state 形成的 jsx 对象和 current Fiber 树对比构建 workInProgress Fiber 树, 这个对比的过程就是 diff 算法。

在 mount 时(首次渲染),会根据 jsx 对象(Class Component 或的 render 函数者 Function Component 的返回值),构建 Fiber 对象,形成 Fiber 树, 然后这颗 Fiber 树会作为 current Fiber 应用到真实 dom 上,在 update(状态更新时如 setState)的时候,会根据状态变更后的 jsx 对象和 current Fiber 做对比形成新的 workInProgress Fiber, 然后 workInProgress Fiber 切换成 current Fiber 应用到真实 dom 就达到了更新的目的,而这一切都是在内存中发生的,从而减少了对 dom 好性能的操作。

diff 算法

diff 算法发生在 render 阶段的 reconcileChildFibers 函数中,diff 算法分为单节点的 diff 和多节点的 diff(例如一个节点中包含多个子节点就属于多节点的 diff),单节点会根据节点的 key 和 type,props 等来判断节点是复用还是直接新创建节点,多节点 diff 会涉及节点的增删和节点位置的变化

reconcile 时会在这些 Fiber 上打上 Flags 标签,在 commit 阶段把这些标签应用到真实 dom 上,这些标签代表节点的增删改,如

1//ReactFiberFlags.js
2export const Placement = /* */ 0b0000000000010;
3export const Update = /* */ 0b0000000000100;
4export const PlacementAndUpdate = /* */ 0b0000000000110;
5export const Deletion = /* */ 0b0000000001000;

updateQueue

Fiber 还保存了更新状态时用于计算 state 的 updateQueue,updateQueue 是一种链表结构,上面可能存在多个未计算的 update,update 也是一种数据结构,上面包含了更新的数据、优先级等,除了这些之外,上面还有和副作用有关的信息。

effectList

effectList 生成方式示例如下:

1function App() {
2 const [count, setCount] = useState(0);
3 return (
4 <>
5 <h1
6 onClick={() => {
7 setCount(() => count + 1);
8 }}
9 >
10 <p title={count}>{count}</p> John
11 </h1>
12 </>
13 );
14}

当点击 h1 时,会生成红色线条的 effectList

effectlist

Renderer(渲染器)

Renderer 发生在 commit 阶段,commit 阶段遍历 effectList 执行对应的 dom 操作或部分生命周期。

Renderer 是在 commit 阶段工作的,commit 阶段会遍历 render(reconciler)阶段形成的 effectList,并执行真实 dom 节点的操作和一些生命周期,不同平台对应的 Renderer 不同,例如浏览器对应的就是 react-dom。

commit 阶段发生在 commitRoot 函数中,该函数主要遍历 effectList,分别用三个函数来处理 effectList 上的节点,这三个函数是 commitBeforeMutationEffects、commitMutationEffects、commitLayoutEffects

concurrent

它是一类功能的合集(如 fiber、schduler、lane、suspense),其目的是为了提高应用的响应速度,使应用 cpu 密集型的更新不在那么卡顿,其核心是实现了一套异步可中断、带优先级的更新。

我们知道一般浏览器的 fps 是 60Hz,也就是每 16.6ms 会刷新一次,而 js 执行线程和 GUI 也就是浏览器的绘制是互斥的,因为 js 可以操作 dom,影响最后呈现的结果,所以如果 js 执行的时间过长,会导致浏览器没时间绘制 dom,造成卡顿。react17 会在每一帧分配一个时间(时间片)给 js 执行,如果在这个时间内 js 还没执行完,那就要暂停它的执行,等下一帧继续执行,把执行权交回给浏览器去绘制。

最后

以上就是 react 的核心架构解析,详细的每个块的分析可以查阅其他文章 😬

More articles from Favori 重剑

react核心api和jsx

为什么要有jsx,为什么会有虚拟bom

September 1st, 2021 · 1 min read

setState是同步的还是异步的

react-setstate-usestate

August 31st, 2021 · 1 min read
© 2016–2023 Favori 重剑
Link to $https://github.com/yuanguandongLink to $https://favori.zcool.com.cn/Link to $https://codepen.io/favori