React-源码解析-生命周期(自定义组件)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介:
image

自定义组件(生命周期)


createClass 是创建自定义组件的入口方法,负责管理生命周期中的 getDefaultProps。该方 法在整个生命周期中只执行一次,这样所有实例初始化的 props 将会被共享。 通过 createClass 创建自定义组件,利用原型继承 ReactClassComponent 父类,按顺序合并 mixin,设置初始化 defaultProps,返回构造函数。 当使用 ES6 classes 编写 React 组件时,class MyComponent extends React.Component 其实就 是调用内部方法 createClass 创建组件


 /**
 * 创建自定义组件
 */ function createClass(spec) {
 //创建自定义组件 var Constructor = identity(function (props, context, updater) {

 // 自动绑定 if (this.__reactAutoBindPairs.length) {
 bindAutoBindMethods(this);
 }

 this.props = props;
 this.context = context;
 this.refs = emptyObject;
 this.updater = updater || ReactNoopUpdateQueue;

 this.state = null;
 //ReactClass没有构造器,通过getInitialState和componentWillMount来代替 var initialState = this.getInitialState ? this.getInitialState() : null;
 this.state = initialState;
 });
 //原型链继承父类
 Constructor.prototype = new ReactClassComponent();
 Constructor.prototype.constructor = Constructor;
 Constructor.prototype.__reactAutoBindPairs = [];
 //合并mixin
 injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));

 mixSpecIntoComponent(Constructor, IsMountedMixin);
 mixSpecIntoComponent(Constructor, spec);

 // 所有的mixin合并后初始化defaultProps(在整个生命周期中,getDefaultProps只执行一次) if (Constructor.getDefaultProps) {
 Constructor.defaultProps = Constructor.getDefaultProps();
 }

 // 减少查找并设置原型的时间 for (var methodName in ReactClassInterface) {
 if (!Constructor.prototype[methodName]) {
 Constructor.prototype[methodName] = null;
 }
 }

 return Constructor;
 }
复制代码

生命周期-挂载阶段(MOUNTING)


  1. mountComponent 负责管理生命周期中的 getInitialState、componentWillMount、render 和 componentDidMount。
  2. 由于 getDefaultProps 是通过构造函数进行管理的,所以也是整个生命周期中最先开始执行的。而 mountComponent 只能望洋兴叹,无法调用到 getDefaultProps。这就解释了为何 getDefaultProps只执行一次。
  3. 由于通过 ReactCompositeComponentBase 返回的是一个虚拟节点,所以需要利用 instantiateReactComponent去得到实例,再使用 mountComponent 拿到结果作为当前自定义元素的结果。
  4. 通过 mountComponent 挂载组件,初始化序号、标记等参数,判断是否为无状态组件,并进行对应的组件初始化工作,比如初始化 props、context 等参数。利用 getInitialState 获取初始化state、初始化更新队列和更新状态。
  5. 若存在 componentWillMount,则执行。如果此时在 componentWillMount 中调用 setState 方法, 是不会触发 re-render的,而是会进行 state 合并,且 inst.state = this._processPendingState(inst.props, inst.context) 是在 componentWillMount 之后执行的, 因此 componentWillMount 中的 this.state 并不是最新的,在 render 中才可以获取更新后的 this.state。因此,React 是利用更新队列 this._pendingStateQueue 以及更新状态 this._pendingReplaceState 和 this._pendingForceUpdate 来实现 setState 的异步更新机制。
  6. 当渲染完成后,若存在 componentDidMount,则调用。这就解释了 componentWillMount 、render 、componentDidMount 这三者之间的执行顺序。
  7. mountComponent 本质上是通过递归渲染内容的,由于递归的特性,父组件的componentWillMount 在其子组件的 componentWillMount 之前调用,而父组件的 componentDidMount在其子组件的 componentDidMount 之后调用。

 /**
 * 初始化组件,渲染标记,注册事件监听器
 */
 mountComponent: function(transaction,hostParent, hostContainerInfo, context, ) {
 //当前元素对应的上下文 this._context = context;
 this._mountOrder = nextMountID++;
 this._hostParent = hostParent;
 this._hostContainerInfo = hostContainerInfo;
 var publicProps = this._currentElement.props;
 var publicContext = this._processContext(context);
 var Component = this._currentElement.type;
 var updateQueue = transaction.getUpdateQueue();
 var doConstruct = shouldConstruct(Component);
 //初始化公共类 var inst = this._constructComponent(doConstruct, publicProps, publicContext,updateQueue,);
 var renderedElement;
 // 用于判断组件是否为stateless,无状态组件没有状态更新队列,只专注渲染 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);
 //初始化state var initialState = inst.state;
 if (initialState === undefined) {
 inst.state = initialState = null;
 }
 //初始化更新队列 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,);
 }
 //如果存在componentDidMount,则调用 if (inst.componentDidMount) {
 transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
 }
 return markup;
 },
 
 performInitialMountWithErrorHandling: function( renderedElement, hostParent, hostContainerInfo, transaction, context, ) {
 var markup;
 var checkpoint = transaction.checkpoint();
 try {
 //捕捉错误,如果没有错误,则初始化挂载
 markup = this.performInitialMount( renderedElement, hostParent, hostContainerInfo, transaction, context, );
 } catch (e) {
 transaction.rollback(checkpoint);
 this._instance.unstable_handleError(e);
 if (this._pendingStateQueue) {
 this._instance.state = this._processPendingState( this._instance.props, this._instance.context, );
 }
 checkpoint = transaction.checkpoint();
 //如果捕捉到错误,则执行unmountComponent()后在初始化挂载 this._renderedComponent.unmountComponent(true);
 transaction.rollback(checkpoint);
 markup = this.performInitialMount( renderedElement, hostParent, hostContainerInfo, transaction, context, );
 }
 return markup;
 },

 performInitialMount: function( renderedElement, hostParent, hostContainerInfo, transaction, context, ) {
 var inst = this._instance;
 var debugID = 0;
 //如果存在componentWillMount() 就调用 if (inst.componentWillMount) {
 inst.componentWillMount();
 //在componentWillMount时调用setState,是不会触发re-render的,而是自动合并 if (this._pendingStateQueue) {
 inst.state = this._processPendingState(inst.props, inst.context);
 }
 }
 //如果不是无状态组件,即可开始渲染 if (renderedElement === undefined) {
 renderedElement = this._renderValidatedComponent();
 }
 var nodeType = ReactNodeTypes.getType(renderedElement);
 this._renderedNodeType = nodeType;
 // 得到 _currentElement 对应的 component 类实例 var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY );
 this._renderedComponent = child;
 //render递归渲染 var markup = ReactReconciler.mountComponent( child, transaction, hostParent, hostContainerInfo,
 this._processChildContext(context), debugID,);
 return markup;
 },

复制代码

生命周期-修改阶段(RECEIVE_PROPS)


  1. updateComponent 负责管理生命周期中的 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render和 componentDidUpdate。
  2. 首先通过 updateComponent 更新组件,如果前后元素不一致,说明需要进行组件更新。
  3. 若存在 componentWillReceiveProps,则执行。如果此时在 componentWillReceiveProps 中调用 setState,是不会触发 re-render 的,而是会进行 state 合并。 且在 componentWillReceiveProps、shouldComponentUpdate 和 componentWillUpdate 中也还是无法获取到更新后的 this.state, 即此时访问的 this.state 仍然是未更新的数据,需要设置 inst.state = nextState 后才可以,因此只有在 render 和 componentDidUpdate 中才能获取到更新后的 this.state。
  4. 调用 shouldComponentUpdate 判断是否需要进行组件更新,如果存在 componentWillUpdate,则执行。updateComponent 本质上也是通过递归渲染内容的,由于递归的特性,父组件的 componentWillUpdate 是在其子组件的 componentWillUpdate 之前调用的,而父组件的 componentDidUpdate也是在其子组件的 componentDidUpdate 之后调用的。
  5. 当渲染完成之后,若存在 componentDidUpdate,则触发,这就解释了 componentWillReceiveProps、componentWillUpdate、render、componentDidUpdate 它们之间的执行顺序。

 // receiveComponent 是通过调用 updateComponent 进行组件更新的
 receiveComponent: function(nextElement, transaction, nextContext) {
 var prevElement = this._currentElement;
 var prevContext = this._context;
 this._pendingElement = null;
 this.updateComponent(transaction,prevElement,nextElement,prevContext,nextContext,);
 },
 
 
 updateComponent: function(transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext,) {
 var inst = this._instance;
 var willReceive = false;
 var nextContext;

 // 上下文是否改变 if (this._context === nextUnmaskedContext) {
 nextContext = inst.context;
 } else {
 nextContext = this._processContext(nextUnmaskedContext);
 willReceive = true;
 }

 var prevProps = prevParentElement.props;
 var nextProps = nextParentElement.props;

 // 新旧属性不同 if (prevParentElement !== nextParentElement) {
 willReceive = true;
 }
 //新旧属性不同,并且存在componentWillReceiveProps,就执行componentWillReceiveProps() if (willReceive && inst.componentWillReceiveProps) {
 inst.componentWillReceiveProps(nextProps, nextContext);
 }
 //将新的state合并到更新队列中,此时的nextState是最新的state var nextState = this._processPendingState(nextProps, nextContext);
 var shouldUpdate = true;
 //根据更新队列和shouldComponentUpdate的状态来判断是否需要更新组件 if (!this._pendingForceUpdate) {
 if (inst.shouldComponentUpdate) {
 shouldUpdate = inst.shouldComponentUpdate(nextProps,nextState,nextContext,);
 } else {
 if (this._compositeType === CompositeTypes.PureClass) {
 shouldUpdate =!shallowEqual(prevProps, nextProps) ||!shallowEqual(inst.state, nextState);
 }
 }
 }

 this._updateBatchNumber = null;
 if (shouldUpdate) {
 //重置更新队列 this._pendingForceUpdate = false;
 //即将更新this.props,this.state,和this.context this._performComponentUpdate(nextParentElement,nextProps, nextState,nextContext,transaction,nextUnmaskedContext,);
 } else {
 // 如果确定组件不更新,仍然要这是props和state this._currentElement = nextParentElement;
 this._context = nextUnmaskedContext;
 inst.props = nextProps;
 inst.state = nextState;
 inst.context = nextContext;
 }
 },
 //在componentShouldUpdate()方法执行前调用了:就是说在调用之前将所有state操作合并
 _processPendingState: function(props, context) {
 var inst = this._instance;
 var queue = this._pendingStateQueue;
 var replace = this._pendingReplaceState;
 this._pendingReplaceState = false;
 this._pendingStateQueue = null;
 //如果队列为null,返回原state if (!queue) {
 return inst.state;
 }
 //如果队列中有一个更新就返回这个更新值 if (replace && queue.length === 1) {
 return queue[0];
 }
 //如果队列中有多个更新,就将他们合并 var nextState = Object.assign({}, replace ? queue[0] : inst.state);
 for (var i = replace ? 1 : 0; i < queue.length; i++) {
 var partial = queue[i];
 Object.assign(
 nextState,
 typeof partial === 'function'
 ? partial.call(inst, nextState, props, context)
 : partial,
 );
 }
 
 return nextState;
 },
 
 /**
 * 当组件需要更新时,调用
 */
 _performComponentUpdate: function(nextElement,nextProps,nextState,nextContext,transaction,unmaskedContext) {
 var inst = this._instance;
 var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
 var prevProps;
 var prevState;
 var prevContext;
 //如果存在componentDidUpdate,则将当前的props,state和context保存一份 if (hasComponentDidUpdate) {
 prevProps = inst.props;
 prevState = inst.state;
 prevContext = inst.context;
 }
 //执行componentWillUpdate if (inst.componentWillUpdate) {
 inst.componentWillUpdate(nextProps, nextState, nextContext);
 }
 //更新this.props,this.state,this.context this._currentElement = nextElement;
 this._context = unmaskedContext;
 inst.props = nextProps;
 inst.state = nextState;
 inst.context = nextContext;
 //渲染组件 this._updateRenderedComponent(transaction, unmaskedContext);
 //当组件完成更新后,如果存在ComponentDidUpdate,则调用 if (hasComponentDidUpdate) {
 transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst,prevProps, prevState,prevContext,),inst,);
 }
 },
 
 /**
 * 调用render渲染组件
 */
 _updateRenderedComponent: function(transaction, context) {
 var prevComponentInstance = this._renderedComponent;
 var prevRenderedElement = prevComponentInstance._currentElement;
 var nextRenderedElement = this._renderValidatedComponent();

 var debugID = 0;
 //如果需要更新,则调用ReactReconciler.receiveComponent()继续更新组件 if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
 ReactReconciler.receiveComponent(prevComponentInstance,nextRenderedElement,transaction,this._processChildContext(context),);
 } else {
 //如果不需要更新,则渲染组件 var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
 ReactReconciler.unmountComponent(prevComponentInstance, false);

 var nodeType = ReactNodeTypes.getType(nextRenderedElement);
 this._renderedNodeType = nodeType;
 //得到 nextRenderedElement 对应的 component 类实例 var child = this._instantiateReactComponent( nextRenderedElement,nodeType !== ReactNodeTypes.EMPTY );
 this._renderedComponent = child;
 //使用render递归渲染 var nextMarkup = ReactReconciler.mountComponent(child,transaction,this._hostParent,this._hostContainerInfo,this._processChildContext(context),debugID,);

 this._replaceNodeWithMarkup(oldHostNode, nextMarkup,prevComponentInstance,);
 }
 },
复制代码

生命周期-卸载阶段(UNMOUNTING)


  1. unmountComponent 负责管理生命周期中的 componentWillUnmount。
  2. 如果存在 componentWillUnmount,则执行并重置所有相关参数、更新队列以及更新状态,如果此时在 componentWillUnmount 中调用 setState,是不会触发 re-render 的, 这是因为所有更新队列和更新状态都被重置为 null,并清除了公共类,完成了组件卸载操作

 unmountComponent: function(safely) {
 if (!this._renderedComponent) {
 return;
 }

 var inst = this._instance;
 //如果存在componentWillUnmount,则调用 if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) {
 inst._calledComponentWillUnmount = true;

 if (safely) {
 var name = this.getName() + '.componentWillUnmount()';
 ReactErrorUtils.invokeGuardedCallback(
 name,
 inst.componentWillUnmount.bind(inst),
 );
 } else {
 inst.componentWillUnmount();
 }
 }
 //如果组件已经渲染,则对组件进行componentWillUnmount() if (this._renderedComponent) {
 ReactReconciler.unmountComponent(this._renderedComponent, safely);
 this._renderedNodeType = null;
 this._renderedComponent = null;
 this._instance = null;
 }
 //重置相关参数、更新队列以及更新状态 this._pendingStateQueue = null;
 this._pendingReplaceState = false;
 this._pendingForceUpdate = false;
 this._pendingCallbacks = null;
 this._pendingElement = null;

 this._context = null;
 this._rootNodeID = 0;
 this._topLevelWrapper = null;
 //删除这个实例
 ReactInstanceMap.remove(inst);
 },
复制代码

无状态组件


  1. 无状态组件只有一个render方法,并没有组件类的实例化过程,也没有实例返回
  2. 无状态组件没有状态,没有生命周期,就是接受props渲染生成dom结构是一个纯粹为渲染而生的组件,
//无状态组件只有一个render方法 function StatelessComponent(Component) {}
StatelessComponent.prototype.render = function() {
 var Component = ReactInstanceMap.get(this)._currentElement.type;
 //没有state状态 var element = Component(this.props, this.context, this.updater);
 return element;
};
复制代码

原文发布时间:2018-06-22

原文作者:mazy

本文来源掘金如需转载请紧急联系作者

相关文章
|
13天前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
63 29
|
9天前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
27 3
|
12天前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
11天前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
5月前
|
前端开发 JavaScript 开发者
深入理解React Hooks:提升前端开发效率的关键
【10月更文挑战第5天】深入理解React Hooks:提升前端开发效率的关键
|
4月前
|
前端开发 JavaScript 开发者
颠覆传统:React框架如何引领前端开发的革命性变革
【10月更文挑战第32天】本文以问答形式探讨了React框架的特性和应用。React是一款由Facebook推出的JavaScript库,以其虚拟DOM机制和组件化设计,成为构建高性能单页面应用的理想选择。文章介绍了如何开始一个React项目、组件化思想的体现、性能优化方法、表单处理及路由实现等内容,帮助开发者更好地理解和使用React。
135 9
|
5月前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
5月前
|
前端开发 JavaScript API
探索React Hooks:前端开发的革命性工具
【10月更文挑战第5天】探索React Hooks:前端开发的革命性工具
|
4月前
|
监控 前端开发 数据可视化
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
@icraft/player-react 是 iCraft Editor 推出的 React 组件库,旨在简化3D数字孪生场景的前端集成。它支持零配置快速接入、自定义插件、丰富的事件和方法、动画控制及实时数据接入,帮助开发者轻松实现3D场景与React项目的无缝融合。
319 8
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
|
4月前
|
前端开发 JavaScript 开发者
使用React和Redux构建高效的前端应用
使用React和Redux构建高效的前端应用
83 2

推荐镜像

更多