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。
該圖省略了事務、事件機制和生命週期等方面的內容,這些會在以後的文章單獨介紹。
參考資料