原文链接:链接
React实例化生命周期可以说相当简单了,想象你随随便便都能背出来。但是!要是从源码来追踪、并分析的话可以说是相当复杂了,不信你往下看。
首先得从元祖ReactElement说起,知道ReactElement是个啥玩意儿么?他就是常说的virtual dom。
在react中,ReactElement(常说的virtual dom) 可以通过react定义的方法createElement或者createClass创建。
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;
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
};
如下代码调用createElement
ReactDOM.render(
<h1>Hello World</h1>,
document.getElementById('root')
);
createClass源码(删减):
function createClass(spec) {
var Constructor = identity(function(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
this.state = null;
var initialState = this.getInitialState ? this.getInitialState() : null;
this.state = initialState;
});
Constructor.prototype = new ReactClassComponent();
Constructor.prototype.constructor = Constructor;
Constructor.prototype.__reactAutoBindPairs = [];
injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));
mixSpecIntoComponent(Constructor, IsMountedPreMixin);
mixSpecIntoComponent(Constructor, spec);
mixSpecIntoComponent(Constructor, IsMountedPostMixin);
if (Constructor.getDefaultProps) {
Constructor.defaultProps = Constructor.getDefaultProps();
}
return Constructor;
}
如下代码调用createClass
ReactDOM.render(
<App />
document.getElementById('root')
);
createElement创建的是DOM Element,而createClass创建的是Component Elements。
当ReactElement的type属性为字符串时,它代表是DOM Element,如下:
{
type: 'button',
props: {
className: 'button button-blue',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}
当ReactElement的type属性为一个函数或一个类时,它代表Component Elements,如下:
class Button extends React.Component {
render() {
const { children, color } = this.props;
return {
type: 'button',
props: {
className: 'button button-' + color,
children: {
type: 'b',
props: {
children: children
}
}
}
};
}
}
// Component Elements
{
type: Button,
props: {
color: 'blue',
children: 'OK!'
}
}
到这里可以得出第一个结论:
- 组件生命周期函数getDefaultProps、getInitialState是在createClass里调用的。
react通过调用createClass或者createElement,生成一个ReactElement。接下来我们探讨ReactElement的走向和变装。
在_renderNewRootComponent函数中,ReactElement经过了一个工厂函数instantiateReactComponent的打包。
var componentInstance = instantiateReactComponent(nextElement, false);
我们看一下instantiateReactComponent的关键代码:
function instantiateReactComponent(node, shouldHaveDebugID) {
var instance;
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
var type = element.type;
if (typeof element.type === 'string') {
instance = ReactHostComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
instance = new element.type(element);
if (!instance.getHostNode) {
instance.getHostNode = instance.getNativeNode;
}
} else {
instance = new ReactCompositeComponentWrapper(element);
}
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactHostComponent.createInstanceForText(node);
} else {
invariant(false, 'Encountered invalid React node of type %s', typeof node);
}
instance._mountIndex = 0;
instance._mountImage = null;
return instance;
}
我们已经知道ReactElement有不同的type,instantiateReactComponent根据这个type的不同,调用相应的ReactComponent构造函数。
这里我把ReactComponent分成4类:
- ReactDOMEmptyComponent:空对象
- ReactDOMComponent:DOM原生对象
- ReactCompositeComponent:React自定义对象
- ReactDOMTextComponent:文本对象
值得注意的是,instantiateReactComponent并不直接生成一个ReactComponent,而是将ReactElement包装成一个 ReactCompositeComponentWrapper,包在了一个容器中。因此,ReactCompositeComponentWrapper除了有他自己的属性外,还能调用生成他的构造函数的一些方法(这样设计一方面就是为了共享这些方法)。
这个所谓的容器长这样:
{
_calledComponentWillUnmount: false
_compositeType: null
_context: null
_currentElement: {$$typeof: Symbol(react.element), key: null, ref: null, props: {…}, type: ƒ, …}
_debugID: 0
_hostContainerInfo: null
_hostParent: null
_instance: null
_mountImage: null
_mountIndex: 0
_mountOrder: 0
_pendingCallbacks: null
_pendingElement: null
_pendingForceUpdate: false
_pendingReplaceState: false
_pendingStateQueue: null
_renderedComponent: null
_renderedNodeType: null
_rootNodeID: 0
_topLevelWrapper: null
_updateBatchNumber: null
_warnedAboutRefsInRender: false
__proto
}
这里先留一个疑问:为什么要把ReactElement放在容器中?
继续,_constructComponent是ReactCompositeComponentWrapper(实例)的构造函数中的一个方法,这个方法能生成一个ReactComponent,这个ReactComponent就是我们写的组件的实例。
var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
一个inst长这样:
{
context: undefined
props: {propsData: "propsData"}
refs: {}
state: {stateData: "stateData"}
updater: ...
enqueueSetState: ...
isMounted: (...)
replaceState: (...)
__proto__: ReactComponent
}
之后,在performInitialMount方法中悄悄的执行了inst.componentWillMount();
,componentWillMount就是在这里跟render和componentDidMount先走一步的。
再之后,还是在mountComponent中,有这么一段代码:
if (inst.componentDidMount) {
if (process.env.NODE_ENV !== 'production') {
transaction.getReactMountReady().enqueue(function () {
measureLifeCyclePerf(function () {
return inst.componentDidMount();
}, _this._debugID, 'componentDidMount');
});
} else {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
}
}
此时但是if (inst.componentDidMount)
肯定为true,但是他并不会执行componentDidMount()
,这是为什么呢?我们往下走。
mountComponent返回的是一个markup,一个markup长这样:
{
children:[]
html:null
node:h1
text:null
toString:ƒ toString()
__proto__:Object
}
markup经过mountComponentIntoNode、_mountImageIntoNode,执行setInnerHTML(container, markup);
终于渲染出来了!!!蓝瘦香菇有木有啊!!!
之后其实经过Transaction里的perform方法的,经过之后transaction.getReactMountReady()相当于true了。这个时候就能访问componentDidMount了。
至此,React初次实例化的一个生命周期分析完毕。起立、敬礼。
经过以上分析,才能得出以下众所周知的总结:
当组件(由createClass创建)在客户端被实例化,第一次被创建时,以下方法依次被调用:
- getDefaultProps
- getInitialState
- componentWillMount
- render
- componentDidMount
网友评论