React 原理分析

优质博文:IT-BLOG-CN

一、简介

为什么我要用react?JQuery也挺好的呀?

1、因为浏览器和JavaScript一直在更新,新版前端框架可以更好对接新的API,更好的利用浏览器的能力, 提供更新潮强大的功能。

2、react有其庞大的生态系统,如插件、工具、库等,对开发者更省力

二、React主要发展历程

2011 Facebook内部使用

2013 Facebook开源

2015 Facebook发布React Native(干掉了iOS和Android开发岗位)

2017 React发布Fiber架构

2019 React16.8引入Hooks

2022 React 18 发布Concurrent Mode和Server Component

2024 React 19RC发布

三、React特性

1、组件化: 代码复用度高
2、虚拟DOM: 减少不必要的DOM操作和渲染
3、JSX: 易于理解和使用
4、跨平台: ReactNative
5、Concurrent Mode: 可中断的异步更新

四、React原理

React15架构:

1、Reconciler(协调器)------ 负责找出变化的组件

2、Renderer(渲染器)------ 负责将变化的组件渲染到页面上

此架构弊端: Reconciler是递归渲染,过程不可中断,当更新任务过大用时过久就会造成页面卡顿

React16架构:

1、Scheduler(调度器)------ 调度任务的优先级,高优任务优先进入

2、ReconcilerReconciler(协调器)------ 负责找出变化的组件

3、Renderer(渲染器)------ 负责将变化的组件渲染到页面上

此架构基于Fiber,时间切片,优先级调度,实现了渲染过程的可中断更新,在浏览器资源上限内最高效率流畅更新页面。

实现步骤:

1.虚拟DOM一个用来描述 UI 结构的 JavaScript 对象,用于抽象表示浏览器中真实的DOM节点,体积轻量,不绑定实际DOM具有的属性和方法。

java 复制代码
// 虚拟 DOM 对象
const virtualDOM = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  }
};

虚拟DOM的作用

每一个浏览器引擎都包含渲染引擎和JS引擎,二者各司其职。JS引擎执行JS快速不费力,可对用户不可见。

渲染引擎渲染页面比较耗费浏览器资源,直接和用户交互。

在接收到DOM更新任务时,虚拟DOM可作为真实DOM替身,缓冲计算好,最后统一更新到真实DOM树上,减少不必要的对DOM的操作和渲染。

当虚拟DOM有唯一标识时,还可以实现对应真实DOM节点的复用。

React Fiber 架构

Fiber Node: 一个典型的 Fiber 节点包含以下几个关键属性:
1、type: 组件的类型(例如函数组件、类组件、DOM 元素等)。
key: 用于标识节点的唯一键。
stateNode: 与 Fiber 节点对应的实际 DOM 节点或组件实例。
child: 指向第一个子节点的指针。sibling:指向下一个兄弟节点的指针。
return: 指向父节点的指针。
pendingProps: 新的属性,在 Fiber 树更新时使用。
memoizedProps: 上一次渲染时的属性。
memoizedState: 上一次渲染时的状态。
alternate: 指向当前 Fiber 节点的另一个版本(用于双缓存机制)。

Fiber Tree: 通过每个Fiber的child,sibling,return节点链接形成的链表结构。这种结构使得 React 可以在不影响其他节点的情况下,灵活地插入、删除或更新Fiber节点。React 遍历 Fiber 树时,通常使用深度优先搜索(DFS)。

双缓存机制: 在React中最多会同时存在两棵Fiber树。当前屏幕上显示内容对应的Fiber树称为current Fiber树,正在内存中构建的Fiber树称为workInProgress Fiber树。

current Fiber树中的Fiber节点被称为current fiber,workInProgress Fiber树中的Fiber节点被称为workInProgress fiber,他们通过alternate属性连接。

React应用的根节点通过使current指针在不同Fiber树的rootFiber间切换来完成current Fiber树指向的切换。

即当workInProgress Fiber树构建完成交给Renderer渲染在页面上后,应用根节点的current指针指向workInProgress Fiber树,此时workInProgress Fiber树就变为current Fiber树。

这种在内存中构建并直接替换的技术叫做双缓存

时间切片

前面提到的每一个浏览器引擎都包含渲染引擎和JS引擎,这两个引擎的工作是互斥的,同一时刻只能工作一个。

浏览器的刷新频率受限于显示器的刷新频率。以60Hz为例,浏览器每1000ms刷新16次,说明浏览器的一个刷新周期是16.6ms。

在浏览器每个刷新周期内,会按次序执行以下工作,JS脚本执行 ----- 样式布局 ----- 样式绘制

其中的JS脚本执行时间如果过长,就会导致刷新周期内无法执行样式绘制,形成掉帧,造成用户视觉上的页面卡顿。

为了解决这种问题,于是诞生了时间切片。

React,在浏览器每一帧刷新周期内,预留一些时间给 JS 线程,利用这部分时间更新组件(可以看到,在源码中,预留的初始时间是 5ms)。

当预留的时间不足以执行完全部的JS时,React将线程控制权交还给浏览器使其有时间渲染 UI,React则等待下一帧时间到来继续被中断的工作。

java 复制代码
function performUnitOfWork(fiber) {  
	// 处理当前 Fiber 节点  
	beginWork(fiber);  

	// 如果有子节点,处理子节点  
	if (fiber.child) {    
		return fiber.child;  
	} 
	 
	// 如果没有子节点,处理兄弟节点或返回父节点  
	let nextFiber = fiber;  
	while (nextFiber) {    
		completeWork(nextFiber);    
		if (nextFiber.sibling) {      
			return nextFiber.sibling;    
		}    
		nextFiber = nextFiber.return;  
	}
}

function workLoop(deadline) {  
	let shouldYield = false;  
	while (nextUnitOfWork && !shouldYield) {    
		nextUnitOfWork = performUnitOfWork(nextUnitOfWork);  
		shouldYield = deadline.timeRemaining() < 1;  
	}  

	if (!nextUnitOfWork) {    
		commitRoot();  
	} else {    
		requestIdleCallback(workLoop);  
	}
}

// 开始渲染
requestIdleCallback(workLoop);

优先级调度

优先级级别: React 定义了多个优先级级别,用于区分不同类型的任务。主要的优先级级别包括:
1、Immediate: 立即执行的任务,通常用于用户输入等需要立刻响应的操作。如文本框输入、按钮点击等。
2、User-blocking: 用户阻塞任务,稍低于立即执行的任务,但仍然需要快速响应,影响用户界面交互流畅性。如拖拽、滑动等。
3、Normal: 正常优先级任务,常规更新,如组件的状态更新、网络请求完成后的数据处理等。Low:低优先级任务,如后台数据同步,定时器。
4、Idle: 空闲优先级任务,只有在浏览器空闲时才会执行。

优先级实现: lane模型借鉴了同样的概念,使用31位的二进制表示31条赛道,位数越小的赛道优先级越高,某些相邻的赛道拥有相同优先级。

调度流程:

1、当需要执行一个任务时,React 会创建一个任务对象,并将其添加到优先级队列中。任务对象包含任务的优先级、执行回调和其他相关信息。

2、调度器会根据任务的优先级对队列中的任务进行排序。高优先级任务会排在队列的前面,低优先级任务则排在后面。

3、调度器会从队列中取出最高优先级的任务并执行它。如果当前有更高优先级的任务到来,调度器会中断当前任务,优先执行高优先级任务。

4、调度器会根据浏览器的空闲时间和任务的优先级来调度任务的执行,确保高优先级任务能够及时响应。(React 使用 requestIdleCallback 和 requestAnimationFrame 等浏览器 API 来实现这一点。

五、流程汇总

相关推荐
捕鲸叉22 分钟前
创建线程时传递参数给线程
开发语言·c++·算法
Devil枫23 分钟前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
A charmer26 分钟前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq29 分钟前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml41 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~1 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616881 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
尚梦1 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
aloha_7891 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
GIS程序媛—椰子2 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js