笔者将编写"React源码解析"系列文章三到四篇,阐述React内部的机制。欢迎大家关注我的掘金账号,以便能及时看到最新的文章更新推送。
在上一篇文章《React源码解析(一)》中,我们阐述了React组件的实现和挂载。现在我们来一起探究组件的生命周期。
我们已经知道,只有在挂载流程开始后,才会触发组件的生命周期,生成
类型的js对象,babel通过解析组件对象内部所携带的信息,获得对应的HTML信息,插入指定的DOM容器中,最终完成视图的渲染。那么组件的生命周期在这一过程中是如何触发的呢
- ReactElement
其实研究组件的声明周期,就是更深入的研究组件的挂载过程。
在上一篇文章的最后,我们知道
方法根据传入的参数不同,在内部通过工厂方法生成四种不同类型的组件:
- ReactDOM.render()
很显然生命周期只在React自定义组件中存在,也就是对应的
。其他三种组件是不存在生命周期的,所以我们先来分析下相对容易的不存在生命周期的三种内部组件。
- ReactCompositeComponent
工厂方法源码如下:
- instantiateReactComponent
- function instantiateReactComponent(node, shouldHaveDebugID) {
- var instance;
- if (node === null || node === false) {
- instance = ReactEmptyComponent.create(instantiateReactComponent);
- } else if (typeof node === 'object') {
- var element = node;
- 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 { ! false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Encountered invalid React node of type %s', typeof node) : _prodInvariant('131', typeof node) : void 0;
- }
- instance._mountIndex = 0;
- instance._mountImage = null;
- return instance;
- }
通过
方法创建,打开
- ReactEmptyComponent.create()
文件:
- node_modules/react/lib/ReactEmptyComponent.js
- var ReactEmptyComponent = {
- create: function(instantiate) {
- return emptyComponentFactory(instantiate);
- }
- };
而
方法最终调用的是:
- emptyComponentFactory
- return new ReactDOMEmptyComponent(instantiate);
再打开
文件:
- node_modules/react/lib/ReactDOMEmptyComponent.js
- var ReactDOMEmptyComponent = function(instantiate) {
- this._currentElement = null;
- this._hostNode = null;
- this._hostParent = null;
- this._hostContainerInfo = null;
- this._domID = 0;
- };
- _assign(ReactDOMEmptyComponent.prototype, {
- mountComponent: function(transaction, hostParent, hostContainerInfo, context) {
- // ...
- },
- receiveComponent: function() {},
- getHostNode: function() {
- return ReactDOMComponentTree.getNodeFromInstance(this);
- },
- unmountComponent: function() {
- ReactDOMComponentTree.uncacheNode(this);
- }
- });
因为组件为空,所以几乎所有参数设置为null,也无关生命周期,只有组件的挂载和卸载,不再赘述。
通过
方法创建,同理根据类名打开对应的文件:
- ReactHostComponent.createInstanceForText()
- function createInstanceForText(text) {
- return new ReactDOMTextComponent(text);
- };
和
- ReactDOMTextComponent
的处理基本相同。
- ReactDOMEmptyComponent
通过
方法创建,同理根据类名打开对应的文件:
- ReactHostComponent.createInternalComponent()
- function createInternalComponent(element) {
- return new ReactDOMComponent(element);
- };
因为dom元素同样没有生命周期,
只对传入的
- ReactDOMComponent
,
- div
等标签进行识别和处理,流程与上述两类组件基本相同。
- span
自定义组件是React组件的重点,接下来我们逐步进行分析。
通过
方法创建,根据类型打开对应的文件:
- ReactCompositeComponentWrapper()
- var ReactCompositeComponentMixin = {
- construct: function(element) {
- //...
- },
- mountComponent: function(transaction, hostParent, hostContainerInfo, context) {
- var publicProps = this._currentElement.props;
- var Component = this._currentElement.type;
- var updateQueue = transaction.getUpdateQueue();
- // Initialize the public class
- var doConstruct = shouldConstruct(Component);
- var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
- var renderedElement;
- // Support functional components
- if (!doConstruct && (inst == null || inst.render == null)) {
- renderedElement = inst;
- warnIfInvalidElement(Component, renderedElement);
- inst = new StatelessComponent(Component);
- this._compositeType = CompositeTypes.StatelessFunctional;
- } else {
- if (isPureComponent(Component)) {
- this._compositeType = CompositeTypes.PureClass;
- } else {
- this._compositeType = CompositeTypes.ImpureClass;
- }
- }
- inst.props = publicProps;
- inst.context = publicContext;
- inst.refs = emptyObject;
- inst.updater = updateQueue;
- this._instance = inst;
- ReactInstanceMap.set(inst, this);
- var initialState = inst.state;
- if (initialState === undefined) {
- inst.state = initialState = null;
- } ! (typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.state: must be set to an object or null', this.getName() || 'ReactCompositeComponent') : _prodInvariant('106', this.getName() || 'ReactCompositeComponent') : void 0;
- this._pendingStateQueue = null;
- this._pendingReplaceState = false;
- this._pendingForceUpdate = false;
- var markup;
- if (inst.unstable_handleError) {
- markup = this.performInitialMountWithErrorHandling(renderedElement, hostParent, hostContainerInfo, transaction, context);
- } else {
- markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
- }
- 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);
- }
- }
- return markup;
- },
- }
代码很长,我们一步一步来:
首先处理传入的props和组件类型,类似
方法:
- getDefaultProps
- var publicProps = this._currentElement.props;
- var Component = this._currentElement.type;
接着根据传入组件类是否包含
方法判定是否是无状态组件:
- render
- if (!doConstruct && (inst == null || inst.render == null)) {
- //...
- inst = new StatelessComponent(Component);
- this._compositeType = CompositeTypes.StatelessFunctional;
- } else {
- if (isPureComponent(Component)) {
- this._compositeType = CompositeTypes.PureClass;
- } else {
- this._compositeType = CompositeTypes.ImpureClass;
- }
- }
存储实例对象的引用到
中,初始化
- ReactInstanceMap
,并判断
- state
的结构是否正确:
- state
- ReactInstanceMap.set(inst, this);
- var initialState = inst.state;
- if (initialState === undefined) {
- inst.state = initialState = null;
- }
- !(typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.state: must be set to an object or null', this.getName() || 'ReactCompositeComponent') : _prodInvariant('106', this.getName() || 'ReactCompositeComponent') : void 0;
接下来开始挂载组件:
- if (inst.unstable_handleError) {
- markup = this.performInitialMountWithErrorHandling(renderedElement, hostParent, hostContainerInfo, transaction, context);
- } else {
- markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
- }
我们重点看下
和
- performInitialMount
方法,他们的区别在于是否有报错信息和相关处理,那么我们只看
- performInitialMountWithErrorHandling
就好:
- performInitialMount
- performInitialMount: function(renderedElement, hostParent, hostContainerInfo, transaction, context) {
- var inst = this._instance;
- if (inst.componentWillMount) {
- if (process.env.NODE_ENV !== 'production') {
- measureLifeCyclePerf(function() {
- return inst.componentWillMount();
- },
- debugID, 'componentWillMount');
- } else {
- inst.componentWillMount();
- }
- // When mounting, calls to `setState` by `componentWillMount` will set
- // `this._pendingStateQueue` without triggering a re-render.
- if (this._pendingStateQueue) {
- inst.state = this._processPendingState(inst.props, inst.context);
- }
- }
- // If not a stateless component, we now render
- if (renderedElement === undefined) {
- renderedElement = this._renderValidatedComponent();
- }
- var nodeType = ReactNodeTypes.getType(renderedElement);
- this._renderedNodeType = nodeType;
- var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY
- /* shouldHaveDebugID */
- );
- this._renderedComponent = child;
- var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID);
- return markup;
- },
首先判断组件类型,如果是无状态组件那么直接调用
方法并返回
- render
类型的对象,接下来如果组件类存在
- ReactElement
方法则执行。接下来执行
- componentWillMount
方法,该方法最终调用
- _renderValidatedComponent
,在babel下执行
- inst.render()
,获得组件对应的HTML,再执行
- React.createElement
方法将其设置为容器的
- setInnerHTML
属性,完成挂载。最后,若自定义的
- innerHTML
方法存在则执行。
- componentDidMount
基于以上,我们可以更新上一篇文章四大组件类型的总结表格:
|
实际参数 | 结果 |
---|---|---|
/
|
空 | 创建组件 |
&&
|
虚拟DOM | 创建组件 |
&&
|
React组件 | 创建组件 |
|
字符串 | 创建组件 |
|
数字 | 创建组件 |
最后基于第一篇分析的文末思维导图,我们进行细节完善:
(点击可查看大图)
来源: https://juejin.im/post/59ca03b9518825177c60d10b