React原理

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
简介: 【4月更文挑战第4天】本文介绍了React的核心概念,包括jsx、React.createElement和fiber。jsx是React的语法糖,被转换为React.createElement生成虚拟DOM (vDOM)以优化性能。vDOM是轻量的数据结构,用于描述DOM状态。React通过fiber结构改进渲染性能,将同步任务拆分成小任务,利用requestIdleCallback在浏览器空闲时执行,确保流畅的用户体验。fiber是增强的vDOM,包含额外的引用指针。文章还提及了diff算法和hooks在React中的作用。

本文主要讲手写React中重要的几个部分,有助于建立对React源码的认知。

  1. CreateElement
    相信大家一定对jsx不陌生
<div title="box">
    <p>jsx</p>
    <span>hhh</span>
</div>

React中的jsx其实就是一个语法糖,上述jsx经过babel翻译后是

React.createElement('div', {
   title: 'box'}, 
    React.createElement('p', {
   }, 'jsx'),
    React.createElement('span', {
   }, 'hhh')
)

React.createElement: (type, props, ...children) => vDom

也就是说我们在写jsx实际上就是在写一个又一个嵌套的React.createElement。只是这样写太难维护了,所以使用了jsx。

React.createElement是干什么的?产生vDom的。
vDom(Virtual DOM),虚拟Dom节点,也就是自定义的一种数据结构,用来对应页面上真实的Dom节点。我们通过操纵vDom来操作真实的节点。
为什么使用vDom?

vDom比真实Dom轻量太多,真实Dom挂载的属性太多,很多根本用不上
可进一步支持跨平台,如RN
vDom结构如下

vDom: {
   
    type,
    props: {
   
        ...props,
        children
    }
}

我们自己写的createElement如下

// 将页面节点分为两类,text和非text
function createElement(type, props, ...children) {
   
    return {
   
        typp,
        props: {
   
            ...props,
            children: children.map(child => typeof child === 'object' ? child: createTextNode(child))
        }
    }
}

// 单独定义text vDom
function createTextNode(text) {
   
    return {
   
        type: 'TEXT',
        props: {
   
            nodeValue: text,
            children: []
        }
    }
}

一切都很清楚了。我们写了一堆jsx以为描述了页面上真实dom的排布,实际上,babel将jsx翻译为了一堆的React.createElement,也就是说,最后我们写的jsx变成了一个vDom树
就拿最开始的例子

<div title="box">
    <p>jsx</p>
    <span>hhh</span>
</div>

====={
   
    type: 'div',
    props: {
   
        title: 'box',
        children: [
            {
   
                type: 'p',
                props: {
   
                    children: [{
   type: 'TEXT', props: {
   nodeValue: 'jsx', children: []}}]
                }
            },
            {
   
                type: 'span',
                props: {
   
                    children: [{
   type: 'TEXT', props: {
   nodeValue: 'jsx', children: []}}]
                }
            }
        ]
    }
}

最后我们得到了上面这个数据结构,它就是我们所描述的页面,下面,就是将这个数据结构渲染成真实dom

  1. fiber
    根据上面的vDom树,直接渲染出真实页面很简单(递归createElement,appendChild),但是存在一个问题,每次render都会重绘整个页面,而这个过程是同步的,很耗时,会阻塞高优先级的任务,比如用户输入,动画之类。
    React的解决办法是:

将长时间的同步任务拆分成多个小任务,从而让浏览器能够抽身去响应其他事件,等他空了再回来继续计算

这个是思路,实现可以使用requestIdleCallback和fiber

requestIdleCallback是一个浏览器实验性API,实现让浏览器空闲的时候来计算(React团队自己实现了这个API)
fiber是一种数据结构,可进行中断和回溯
具体来说,实现如下

nextUnitOfWork和workInProgressRoot是两个全局变量
nextUnitOfWork表示下一个访问的fiber节点
workInProgressRoot也叫wipRoot表示本次渲染的fiber树根节点
performUnitOfWork: (fiber) => fiber,传入要访问(工作)的fiber节点,返回下一个待处理的fiber节点
commitRoot: 提交所有vDom修改,一次性渲染到页面上

function workLoop(deadline) {
   
  while (nextUnitOfWork && deadline.timeRemaining() > 1) {
   
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  }
  if (!nextUnitOfWork && workInProgressRoot) {
   
    commitRoot();
  }

  requestIdleCallback(workLoop);
}

requestIdleCallback(workLoop);

这段代码的意思是:
如果浏览器空闲,且存在待处理fiber,
就会处理该fiber并返回下一个待处理fiber
如果不存在待处理fiber了,而且本次要执行渲染,
就会将修改提交到页面上。
这个工作由浏览器调度,一直持续着。

那fiber到底长什么样呢?首先,说了fiber就是一种数据结构,不要害怕它
fiber我认为就是对vDom的一个扩展。按面向对象来说,可以认为fiber extends vDom

fiber: {
   
    // 和vDom相同的属性
    type,
    props,
    //----
    dom, // 对应的真实dom节点
    child, // 子指针,指向第一个儿子
    sibling, // 兄弟指针,指向后一个相邻兄弟
    return, // 父指针,每个儿子都有
    alternate, // 老的fiber节点,用于diff
    effectTag, // 标记,用于向页面提交更改,REPLACEMENT | UPDATE | DELETION
    hooks // 该fiber上挂载的hook 
}

后面三个属性可以先不看,相信你已经知道fiber长什么样了,就是一棵多了几个指向的树
下面分别来看一下提到的几个函数,performUnitOfWork,commitRoot

结语
React主要涉及这么几个方面:

React.createElement创建vDom
浏览器闲时调度
fiber
diff
hooks

目录
相关文章
|
7月前
|
算法 前端开发 JavaScript
React的diff算法原理
React的diff算法原理
135 0
|
7月前
|
JSON 缓存 前端开发
【React】React原理面试题集锦
本文集合一些React的原理面试题,方便读者以后面试查漏补缺。作者给出自认为可以让面试官满意的简易答案,如果想要了解更深刻,可以点击链接查看对应的详细博文。在此对链接中的博文作者非常感谢🙏。
147 0
|
7月前
|
存储 资源调度 前端开发
React原理 即 React路由基础
React原理 即 React路由基础
90 1
|
2月前
|
存储 前端开发 测试技术
React Hooks 的工作原理
【10月更文挑战第1天】
|
4月前
|
前端开发 算法 JavaScript
React原理之Diff算法
【8月更文挑战第24天】
|
4月前
|
前端开发 JavaScript 算法
如何学习react原理
【8月更文挑战第9天】 如何学习react原理
49 6
|
4月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
43 0
|
4月前
|
前端开发 Java UED
瞬间变身高手!JSF 与 Ajax 强强联手,打造极致用户体验的富客户端应用,让你的应用焕然一新!
【8月更文挑战第31天】JavaServer Faces (JSF) 是 Java EE 标准的一部分,常用于构建企业级 Web 应用。传统 JSF 应用采用全页面刷新方式,可能影响用户体验。通过集成 Ajax 技术,可以显著提升应用的响应速度和交互性。本文详细介绍如何在 JSF 应用中使用 Ajax 构建富客户端应用,并通过具体示例展示 Ajax 在 JSF 中的应用。首先,确保安装 JDK 和支持 Java EE 的应用服务器(如 Apache Tomcat 或 WildFly)。
50 0
|
4月前
|
缓存 JavaScript 前端开发
【React生态进阶】React与Redux完美结合:从原理到实践全面解析构建大规模应用的最佳策略与技巧分享!
【8月更文挑战第31天】React 与 Redux 的结合解决了复杂状态管理的问题,提升了应用性能。本文详细介绍了在 React 应用中引入 Redux 的原因、步骤及最佳实践,包括安装配置、状态管理、性能优化等多方面内容,并提供了代码示例,帮助你构建高性能、易维护的大规模应用。
72 0
|
4月前
|
前端开发 JavaScript 中间件
【前端状态管理之道】React Context与Redux大对决:从原理到实践全面解析状态管理框架的选择与比较,帮你找到最适合的解决方案!
【8月更文挑战第31天】本文通过电子商务网站的具体案例,详细比较了React Context与Redux两种状态管理方案的优缺点。React Context作为轻量级API,适合小规模应用和少量状态共享,实现简单快捷。Redux则适用于大型复杂应用,具备严格的状态管理规则和丰富的社区支持,但配置较为繁琐。文章提供了两种方案的具体实现代码,并从适用场景、维护成本及社区支持三方面进行对比分析,帮助开发者根据项目需求选择最佳方案。
76 0