【Under-the-hood-ReactJS-Part0】React源码解读

算法沙漏
• 阅读 2105

接上文---

完整流程图见:https://bogdan-lyashenko.gith...
继续我们的React之旅,让我们从ReactDOM.render的调用开始。

ReactDOM.render

ReactDOM.render是我们分析的入口点。我们的应用从这里开始渲染内容到DOM树中。为了方便调试,我们创建了一个<ExampleApplication/>的简单组件。整个流程的第一个动作就是把JSX转换成React elements 。 React elements就是带一些架构的简单对象。 他们就是用来表示组件render方法的返回值,除此之外,没有其他的。对象中的一些字段,如props,key,ref对于大家应该已经很熟悉了。 type属性表示的是JSX定义的标记对象,在我们的案例中,就是类 ExampleApplication, 当然,它也可以表示 Button标签的字符串格式等等。 同时,在React element的创建期间,React将会合并defaultProps和props(如果有声明的话),并且严重propTypes。
更多详情请查看源码 srcisomorphicclassicelementReactElement.js

ReactMount

在流程图中,你可以发现有个叫做ReactMount的模块。它包含了整个组件挂载的逻辑。
其实,ReactDOM中是没有任何逻辑的,它不过是一个用来调用ReactMount的接口,所以当调用ReactDOM.render方法时,技术上来说,你真正调用的是ReactMount.render方法。那么整个挂载过程到底是怎么样的呢?

Mounting is the process of initializing a React component by creating its representative DOM elements and inserting them into a supplied container.(挂载是指初始化一个React组件的过程,包括生成组件对应的DOM元素并插入指定的容器中)

以上文字来自于React代码的注释,那么这些到底是一个怎么样的过程呢?我们先看下以下的一个转化:
React需要将你组件里的JSX描述转化为对应的HTML结构,并插入到DOM树中,这个过程,
React需要处理所有的属性,绑定的事件,内嵌的组件和所有逻辑。挂载,就是指把用高层次语言描述的组件(JSX)转化为低层次的html代码,然后插入到DOM树中。

为了让以上描述更具体下,考虑如下需求:

目标:确保滚动事件被监听
在一个根组件的第一次渲染过程中,React会初始化滚动监听事件,并且把滚动条相关数值缓存起来,这样,当应用代码可以在不触发重排(reflow)的前提下,访问到滚动条相关数据。由于不同的游览器会有不同的实现,一些DOM的数值是非固定的,每次当你在代码中获取它们时,它们都有可能会重新计算。显然,这一步骤会引起一些性能问题。比如,一些老的游览器,是不支持pageX和pageY属性的。为了解决这个问题,React会做一些优化,而这些优化过程中,可能就会需要很多其它技巧。在其它问题中,React为了解决某个具体的问题,都会用到很多技巧,滚动条就是一个具体的列子。

实例化React组件

回顾下最开始的流程图,这里有一个实例创建的过程。事实上,目前去创建一个<ExampleApplication>的实例还有点早,这里我们真正实例化的是类TopLevelWrapper(React内部类)。我们先跳过这个过程,看下一个流程。

在JSX的转化过程中,这里有三个阶段。JSX转化成React elements后,React elements会被转化为以下内部React组件类型中的一:ReactCompositeComponent(开发自定义的组件),ReactDOMComponent(HTML DOM节点),ReactDOMTextComponent(文本节点)。我们先忽略ReactDOMTextComponent,重点放在前两个。

什么是内部组件呢?你可能已经听过虚拟DOM。虚拟DOM是一种DOM的表示方式,在React的diff差异计算以及其它过程中,使用虚拟DOM使得可以不直接DOM树,而这恰是React性能不错的原因之一。其实,在React的源码中,并没有什么文件或者类被称作虚拟DOM,因为虚拟DOM只是一种概念,一种用来描述如何处理真实DOM的手段。有些人可能会说虚拟DOM表示的就是React elements,但是我不这么认为。在我看来,虚拟DOM指的是这三个类:ReactCompositeComponent,ReactDOMComponent,ReactDOMTextComponent。稍后我会详细解释原因。

让我们继续组件的实例化。我们会创建一个ReactCompositeComponent的实例,但是,
这个实例并不是因为我们将<ExampleApplication/>放入ReactDOM.render中然后才生成的。React总是从TopLevelWrapper里开始渲染一个组件树。它几乎是一个纯包装组件,它的render方法(组件的render方法)将会返回<ExampleApplication/>。代码如下:

//src\renderers\dom\client\ReactMount.js#277
TopLevelWrapper.prototype.render = function () {
  return this.props.child;
};

根据以上代码,很显然只有一个TopLevelWrapper的实例被创建了,除此之外就没有其它的了。在继续下一步之前,我们看下以下内容:

DOM内嵌验证
几乎每次内嵌组件渲染时,它们都会被一个专门用来做HTML验证的模块--validateDOMNesting--来验证结构是否合法。所谓的DOM内嵌验证是指校验子模块和父模块的标签层次。比如,如果父组件的标签是<select>,子组件的标签只能是以下中的一个:optino,optgroup,#text。 这些规则都是在https://html.spec.whatwg.org/... 被定义的。你很可能已经看到过这个模块的工作成果,它会产生如下的错误提醒:<div>不能为<p>的后代出现(<div> cannot appear as a descendant of <p> .)

好了,让我们回想下之前的内容,然后再回顾下挂载相关的流程图。 Part0的部分就是这些。

(未完待续)

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Java修道之路,问鼎巅峰,我辈代码修仙法力齐天
<center<fontcolor00FF7Fsize5face"黑体"代码尽头谁为峰,一见秃头道成空。</font<center<fontcolor00FF00size5face"黑体"编程修真路破折,一步一劫渡飞升。</font众所周知,编程修真有八大境界:1.Javase练气筑基2.数据库结丹3.web前端元婴4.Jav
Stella981 Stella981
4年前
Discuz X3.2源码解析 discuz_application类(转自百度)
1.discuz\_application在/source/class/discuz/discuz\_application.php中。!DiscuzX3.2源码解析discuz_application类(https://oscimg.oschina.net/oscnet/99b35d79caf70b7c74ad0838d6
Stella981 Stella981
4年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
4年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这