概述
本系列文章將通過React赞弥、ReactDOM的幾個關鍵方法,如 ReactDOM.render
嗦枢、 this.setState
開始,對React源碼進行解讀附鸽。
與一般源碼解析的文章不同脱拼,本系列文章不會在文中一步步通讀代碼,而是通過對關鍵方法的探究坷备,一步步了解React內(nèi)部的原理熄浓。
閱讀前,您應具備React省撑、JSX赌蔑、ES6的實踐經(jīng)驗,同時應了解babel竟秫、chrome斷點調(diào)試等娃惯。
本文所閱讀的React源碼版本為 v15.6.2,可點擊此下載肥败。
從React.createElement及JSX講起
眾所周知趾浅,JSX只是為React.createElement(component, props, ...children)
方法提供的語法糖,如以下代碼(參考文檔):
// JSX語法
let app = (<div id='app'>Hello World!</div>);
在babel編譯后:
// babel編譯后:
let app = React.createElement('div', {id: 'app'}, 'Hello World!');
將 app
在console.log
出來后馒稍,得到以下的結果:
接下來皿哨,我們到源碼中ReactElement.js
查看 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;
if (config != null) {
// 對config參數(shù)的處理纽谒,您可直接查看源碼证膨,此處隱藏
}
// 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) {
// 處理 children多于1個時...
}
// 寫入元素的defaultProps
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);
};
可以看到其調(diào)用了名為 ReactElement
的工廠方法, 該工廠方法主要代碼如下:
var ReactElement = function (type, key, ref, self, source, owner, props) {
var element = {
// 增加一個屬性鼓黔,用來標識這個對象是一個 ReactElement '對象'
$$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
};
if (process.env.NODE_ENV !== 'production') {
// 開發(fā)環(huán)境中的提示...
}
return element;
};
至此椎例,我們在JSX中所寫的元素,都是使用React.createElement
創(chuàng)建ReactElement
實例请祖。
所謂“組件” Component
在Reactv16之前,有三種自定義組件的方式:
// v16.0.0以后已經(jīng)廢棄此方法
var Hello = React.createClass({
render(){
return <p>Hello world, {this.props.guest}!</p>
}
})
// 無狀態(tài)組件推薦
var Bye = function(props){
return (
<p>Bye Bye, {props.guest}!</p>
)
}
// ES6類繼承
class SayHi extends React.Component {
render(){
return <p>Hi, {this.props.guest}!</p>
}
}
我們從ES6類繼承的方式來著手脖祈,查看 React.js
文件肆捕,可以找到 React.Component
實際為 ReactBaseClasses.js
中定義的 ReactComponent
構造函數(shù)(類)
function ReactComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
ReactComponent.prototype.isReactComponent = {};
ReactComponent.prototype.setState = function (partialState, callback) {
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
ReactComponent.prototype.forceUpdate = function (callback) {
this.updater.enqueueForceUpdate(this);
if (callback) {
this.updater.enqueueCallback(this, callback, 'forceUpdate');
}
};
此處可以看到我們熟悉的props、context都是在實例化這個構造函數(shù)時傳入的盖高。
setState
及forceUpdate
都是原型上的方法慎陵,參數(shù)中傳入的 updater
將在我們分析 ReactDOM.render
這個方法的時候詳細分析。
我們知道喻奥,其實class
本質(zhì)上就是原型繼承的語法糖席纽,使用class
定義的 組件 類,實際也是一個 'function'
撞蚕, 與無狀態(tài)組件定義的時候是一樣的润梯。
那么,組件與上一節(jié)所講的ReactElement
有什么聯(lián)系呢?
我們通過以下代碼來分析:
class Hello extends React.Component {
render(){
return (
<div>
<h2>{this.props.title}</h2>
</div>
)
}
}
console.log(<Hello />) // <Hello /> 相當于 React.createElement(Hello)
查看以下結果:
對比上一次的結果纺铭,我們發(fā)現(xiàn)只有type這里是存在差異的寇钉,而這種差異帶來的,就是React在渲染元素舶赔、組件時候的差異了扫倡,這個將放在下一篇,也許是下下篇講竟纳。
關于組件容易混淆的概念
// No.1
class Hello extends React.Component {
render(){
return (
<h2>Hello world!</h2>
)
}
}
// No.2
var HelloCom = <Hello />
// No.3
var HelloEle = (
<div>
<h2>Hello world!</h2>
</div>
)
以上代碼中的 Hello撵溃、HelloCom、HelloEle
哪些是“組件” 锥累?
答案: 只有 Hello
一個缘挑。
首先看 組件的定義: 參考文檔
在這里,HelloCom 揩悄、 HelloEle
實際上已經(jīng)是ReactElement的一個“實例”(說實例不太恰當卖哎,但足夠形象)了,而非如組件定義的:是一個可復用的部件删性,接收props亏娜,并返回元素。
總結
本篇了解了React中ReactElement
和ReactComponent
部分的源碼蹬挺,并從中了解到我們在JSX中所寫的dom在Javascript中會被轉(zhuǎn)換成一個對象维贺。
讀到這里,您可能會想巴帮,那這些對象是怎么被轉(zhuǎn)換并插入到我們的dom結構中呢溯泣?敬請期待下一篇關于ReactDOM.render
的分析。