本文作者:IMWeb chenxd1996 原文出处:IMWeb社区 未经同意,禁止转载
前言: 前段时间笔者对React源码进行了学习,对于React一些原理有了更深的理解,但一些细节可能还不是很清楚,在这里与大家分享一下我对于React源码的一些理解。
本文基于React 15.6,现在React 16.4已经出来了,16版本与之前版本的代码差距还是比较大的,阅读难度也加大了,所以先从15版本入手会更好理解一些。
React源码学习中,我主要是了解了React中两个重要过程的源码。一是初始渲染,二是UI更新,这两部分应该就是React源码的核心了,网上也有很多博客对这两部门的源码进行分析,但推荐大家还是要自己去看源码,因为笔者在看源码之前看了很多篇博客,还是感觉似懂非懂,还是要身体力行啊,别偷懒~
源码解析要说清楚细节很难,篇幅上可能得花上七、八篇博客,所以我在这篇博客中只简单介绍React初始渲染和,略去很多代码,大家有兴趣再细致去阅读源码。
我们先来看下这个例子
这个例子很简单,我们关注到
ReactDOM.render(<App />, document.getElementById('root'));
`<App />
经过babel转译过后,会创建出一个React Element:
这个东东其实就是虚拟DOM了,这里看到虚拟DOM的优点了,因为它比真实的HTML Element少了好多属性,这样创建、销毁虚拟DOM就会比操作真实DOM快很多了。
接下来我们来看下ReactElement这几个属性分别是什么:
$$typeof:标识这个对象是React Element
type: 元素的类型,可能是string 或者 function 或者 class
key: 元素的唯一标识
ref: 元素的ref, 真实Dom的引用
props: 元素的props,例如children,style
_owner:创建这个元素的元素
简略一下,ReactElement就变成这样了:
接下来,React源码会在这个App元素外面包一层TopLevelWrapper,然后创建出一个新的ReactElement
这个TopLevelWrapper其实很简单,我们只要知道它原型上的render方法,会返回this.props.child,而这个this.props.child指的就是前面的App元素。
可能大家会有疑问,做了这一层包装有什么用呢?React源码中的解释是这样的:
简单来说,就是ReactDOM.render(<XX />)
这里的XX可能是类似div,span这样的host组件,也可能是React Component,包一层之后,根组件就可以统一当成React Component来处理了。
下面是TopLevelWrapper元素和APP元素的关系图
接下来,React源码会通过ReactElement(T)去创建内部实例,所谓内部实例,就是React源码中用来管理组件的内部组件实例。
实际返回ReactCompositeComponent实例。
React源码中有三种内部组件:
它们都有以下四个重要方法来管理组件:
下面是ReactCompositeComponent、ReactElement(T)、ReactElement(A)三者的关系图:
接下来通过ReactCompositeComponent,React开始它的初始渲染。
初始渲染分为两个过程
创建真实DOM结点的过程称为挂载,以ReactReconciler.mountComponent作为入口进行递归挂载,不断调用内部实例的mountComponent方法,最后得到一棵DOM子树。
通过挂载后,再将DOM子树插入到容器中,初次渲染完成,Hello World显示在屏幕上。