React也写了有一段时间了,不了解下ta的原理都很差意思和别人说本身会React...因此看了一些源码分析的文章,本身也撸了一遍React的源码【真是有点绕】,算是搞明白了React的原理。javascript
可是最近给一个妹纸解释React原理的时候,把她说蒙圈了...很受伤,本着面向妹纸编程的原则,决定仍是要写成文章,再给她看看。html
【本文基于 React v15.0.0】java
【源码分析部分只保留production环境下的核心功能的代码】react
在react的源码中有三个概念须要先分清楚:编程
ReactElement是描述react中的虚拟的DOM节点的对象,ReactElement主要包含了这个DOM节点的类型(type)、属性(props)和子节点(children)。ReactElement只是包含了DOM节点的数据,尚未注入对应的一些方法来完成React框架的功能。数组
ReactElement经过React.createElement来建立,使用jsx语法的表达式,也会被babel(react的妈妈Facebook在react刚出生的时候是有提供本身的编译器的,可是Babel以后成为了社区主要的jsx语法编译的工具)编译成对应的调用React.createElement的形式。babel
在Babel官网上实验一下比较清楚:app
const React = require('react');
const ReactDOM = require('react-dom');
const View = (
<div className='wrapper--outer' > <div className='wrapper1--inner' style={{ color: '#38f' }} > hello world </div> <div className='wrapper2--inner' > hello world </div> <Hello /> <Inner text="heiheihei"> <div>yoyoyo</div> </Inner> </div>
);
ReactDOM.render(<View />, document.getElementById('app')); 复制代码
Babel编译后:框架
'use strict';
var React = require('react');
var ReactDOM = require('react-dom');
var View = React.createElement(
'div',
{
className: 'wrapper--outer'
},
React.createElement(
'div',
{
className: 'wrapper1--inner',
style: { color: '#38f' }
},
'hello world'
),
React.createElement(
'div',
{
className: 'wrapper2--inner'
},
'hello world'
),
React.createElement(Hello, null),
React.createElement(
Inner,
{ text: 'heiheihei' },
React.createElement(
'div',
null,
'yoyoyo'
)
)
);
ReactDOM.render(React.createElement(View, null), document.getElementById('app'));
复制代码
能够看到在jsx文件中的html写法的表达式都会被编译成调用React.createElement的形式。咱们称呼jsx里面的为React的DOM标签的话,若是DOM标签的首字母为大写的时候,这个标签(类 => 自定义组件类, 函数 => 无状态组件)则会被做为参数传递给createElement;若是DOM标签的首字母为小写,则将标签名(div, span, a 等html的 DOM标签)以字符串的形式传给createElement;若是是字符串或者空的话,则直接将字符串或者null当作参数传递给createElement。dom
React.createElement的源码(具体解释看注释):
ReactElement.createElement = function (type, config, children) {
var propName;
// Reserved names are extracted
var props = {};
var key = null;
var ref = null;
var self = null;
var source = null;
// 将参数赋给props对象
if (config != null) {
ref = config.ref === undefined ? null : config.ref;
key = config.key === undefined ? null : '' + config.key;
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
for (propName in config) {
// 跳过React保留的参数
if (config.hasOwnProperty(propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
}
}
// 将子元素按照顺序赋给children的数组
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// 对于默认的参数,判断是否有传入值,有的话直接将参数和对应的值赋给props,不然将参数和参数默认值赋给props
// Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
};
var ReactElement = function (type, key, ref, self, source, owner, props) {
var element = {
// Symbol类型的tag惟一标示这个对象是一个React Element类型
// This tag allow us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner
};
return element;
};
复制代码
createElement基本没作什么特别的处理,返回了一个React Element对象,因此上述的栗子View最终是一个多层级的大对象(简化以下):
const View = (
<div className='wrapper--outer' > <div className='wrapper1--inner' style={{ color: '#38f' }} > hello world </div> <div className='wrapper2--inner' > hello world {null} </div> <Hello /> <Inner text="heiheihei"> <div>yoyoyo</div> </Inner> </div>
);
---------------
{
type: 'div',
props: {
className: 'wrapper--outer'
},
children: [
{
type: 'div',
props: {
className: 'wrapper1--inner',
style: {
color: '#38f'
}
},
children: 'hello world'
}, {
type: 'div',
props: {
className: 'wrapper2-inner'
},
children: [
'hello world',
null
]
},
{
type: Hello,
props: null
},
{
type: Inner,
props: {
text: 'heiheihei'
},
children: [
{
type: 'div',
props: null,
children: 'yoyoyo'
}
]
}
]
}
复制代码
这样一个对象只是保存了DOM须要的数据,并无对应的方法来实现React提供给咱们的那些功能和特性。ReactElement主要分为DOM Elements和Component Elements两种,咱们称这样的对象为ReactElement。
ReactComponent是基于ReactElement建立的一个对象,这个对象保存了ReactElement的数据的同时注入了一些方法,这些方法能够用来实现咱们熟知的那些React的特性。
ReactClass就是咱们在写React的时候extends至React.Component类的自定义组件的类,如上述中的View和Inner,ReactClass实例化后调用render方法可返回ReactElement。
对于撸源码我的的习惯是代码少的话,直接看就是了;代码量大、复杂的话能够先看些文章,大体了解重点,(过一遍源码,这个看我的兴趣,最有效的仍是打断点执行查看实际运行的流程)而后挑几个表明性的case打断点单步执行把执行过程再过一遍就行了。
React的源码比较绕,用了不少的依赖注入的方式去定义方法,基本上当你不清楚一个方法何时定义的时候,ta可能就是在
(ReactMount.js)
ReactDefaultInjection.inject();
复制代码
中注入的。
这里须要注意的是在React中主要有四类组件:
根据输入的ReactElement的type的类型的不一样instantiateReactComponent方法会返回不一样类型的Component。
在介绍了上面的一些须要了解的基本概念后,用流程图来表示React组件初次渲染以下。
最终经过_mountImageIntoNode一次性将以前递归生成的markup渲染成真实的DOM。
该图省略了事务、事件机制和生命周期等方面的内容,这些会在以后的文章单独介绍。
参考资料