React源码解析(三):react-component

在我们平时写一个类组件的时候,一般都会继承一个React.Component这个基类,我们可能会觉得,这个baseClass可能封装各种各样的功能(钩子函数等等),它帮助我们运行render函数,然后最终不是我们写在里面的dom标签、子组件之类的把它们都渲染到浏览器里的这种形式。但实际是这样的吗?答案是否定的。

在react当中不止有Component这一个baseClass, 它还有一个PureComponent这个baseClass, 它们俩唯一的区别就是PureComponent提供了一个shouldComponentUpdate简单的实现,在props没有变化的情况下减少不必要的更新。

我们先看看这个Component和PureComponent的实现源码:

/** 
* Copyright (c) Facebook, Inc. and its affiliates. 
* 
* This source code is licensed under the MIT license found in the 
* LICENSE file in the root directory of this source tree. 
*/

import invariant from 'shared/invariant';
import lowPriorityWarning from 'shared/lowPriorityWarning';
import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
const emptyObject = {};

if (__DEV__) {  Object.freeze(emptyObject);}
/** 
* Base class helpers for the updating state of a component. 
*/

function Component(props, context, updater) {  
    this.props = props;  
    this.context = context;  
    // If a component has string refs, we will assign a different object later.  
    this.refs = emptyObject;  
    // We initialize the default updater but the real one gets injected by the  

    // renderer.  
    this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};

/** 
* Sets a subset of the state. Always use this to mutate 
* state. You should treat `this.state` as immutable. 
* 
* There is no guarantee that `this.state` will be immediately updated, so 
* accessing `this.state` after calling this method may return the old value. 
* 
* There is no guarantee that calls to `setState` will run synchronously, 
* as they may eventually be batched together.  You can provide an optional 
* callback that will be executed when the call to setState is actually 
* completed. 
* 
* When a function is provided to setState, it will be called at some point in 
* the future (not synchronously). It will be called with the up to date 
* component arguments (state, props, context). These values can be different 
* from this.
* because your function may be called after receiveProps but before 
* shouldComponentUpdate, and this new state, props, and context will not yet be 
* assigned to this. 
* 
* @param {object|function} partialState Next partial state or function to 
*       produce next partial state to be merged with current state. 
* @param {?function} callback Called after state is updated. 
* @final 
* @protected 
*/

Component.prototype.setState = function(partialState, callback) {  
    invariant(    
        typeof partialState === 'object' ||      
        typeof partialState === 'function' ||      
        partialState == null,
        'setState(...): takes an object of state variables to update or a ' +      
        'function which returns an object of state variables.',  
    );  

    this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
/** 
* Forces an update. This should only be invoked when it is known with 
* certainty that we are **not** in a DOM transaction. 
* 
* You may want to call this when you know that some deeper aspect of the 
* component's state has changed but `setState` was not called. 
* 
* This will not invoke `shouldComponentUpdate`, but it will invoke 
* `componentWillUpdate` and `componentDidUpdate`. 
* 
* @param {?function} callback Called after update is complete. 
* @final 
* @protected 
*/

Component.prototype.forceUpdate = function(callback) {  
    this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

/** 
* Deprecated APIs. These APIs used to exist on classic React classes but since 
* we would like to deprecate them, we're not going to move them over to this 
* modern base class. Instead, we define a getter that warns if it's accessed. 
*/

if (__DEV__) {  
    const deprecatedAPIs = {    
        isMounted: [
            'isMounted',
            'Instead, make sure to clean up subscriptions and pending requests in ' +
            'componentWillUnmount to prevent memory leaks.',    
        ],    
        replaceState: [      
            'replaceState',      
            'Refactor your code to use setState instead (see ' +        
            'https://github.com/facebook/react/issues/3236).',    
        ],  
    };  

    const defineDeprecationWarning = function(methodName, info) {    
        Object.defineProperty(Component.prototype, methodName, {      
            get: function() {        
                lowPriorityWarning(          
                    false,          
                    '%s(...) is deprecated in plain JavaScript React classes. %s',          
                    info[0],          
                    info[1],        
                );    
    
                return undefined;      
            },    
        });  
    };  

    for (const fnName in deprecatedAPIs) {    
        if (deprecatedAPIs.hasOwnProperty(fnName)) {      
            defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);    
        }  
    }
}

function ComponentDummy() {}

ComponentDummy.prototype = Component.prototype;

/** 
* Convenience component with default shallow equality check for sCU. 
*/

function PureComponent(props, context, updater) {  
    this.props = props;  
    this.context = context;  
    // If a component has string refs, we will assign a different object later.  
    this.refs = emptyObject;  
    this.updater = updater || ReactNoopUpdateQueue;
}

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());

pureComponentPrototype.constructor = PureComponent;

// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);

pureComponentPrototype.isPureReactComponent = true;

export {Component, PureComponent};复制代码

我们可以看到Component是一个函数,是一个使用函数进行的类声明的一个方式。它接受三个参数props,context,updater,props和context在使用中大家都知道了,那么这个updater什么呢?我们往下看。

我们看到Component原型上挂在了一个方法setState,这个使我们在使用react时最常用的api了,是用来更新我们组件的状态的。它接受两个参数partial和callback,partial可以是一个对象或者一个方法,在后面的版本都推荐使用方法,callback就是在我们这个state更新完之后它会执行callback。

我们可以看到在这个setState方法里,前半部分都是一个提醒,来判断partial这个是否符合预期要求,如果不符合就给出一个错误的提示。重要的是最后这一行代码this.updater.enqueueSetState(this, partialState, callback, 'setState') 其实我们在调用setState方法时,在Component这个对象里面它什么都没有做,它只是调用了Component初始化时传入的updater对象下的enqueueSetState这个方法。enqueueSetState这个方法我们先不讲,它是在react-dom里面实现的,跟react的代码是没有任何关系的。为什么要这么去做呢,因为不用的平台react-native和react-dom它们用的核心是一样的,也就是它们Component的api是一模一样的。但是具体的我们更新state走的流程就是这个具体的渲染的流程是跟平台有关的。react-dom平台和react-native平台它们的实现方式是不一样的,所以这一部分它作为一个参数让不同的平台去传入进来,去定制它的一个实现方法。所以,这就是为什么要使用一个对象传入进来之后在setState里面去调用的原因。具体的实现会在后面的文章中讲道。

加下去我们还会看到Component上还有一个方法叫forceUpdate, 它内部也是调用了this.updater.enqueueSetState(this, callback, 'forceUpdate')这个方法。这个api我们不常用,它的作用就是强制我们Component去更新一遍,即便我们的state没有更新。

然后再往下就是两个即将废弃的api,isMounted 和 replaceState。

至此就是Component定义的全部的内容,没有任何关于生命周期的一些方法,是不是和你想的不一样。

接下来我们来看一下另一个baseClass,它就是PureComponent。我们可以认为它是继承Component,它们接受的东西都是一样的。唯一的区别就是它在PureComponent上加了一个isPureReactComponent, 通过这么一个属性来标识继承自这个类的组件是一个PureComponent。然后在后续的更新当中,react-dom它会主动的去判断它是不是一个PureComponent, 然后根据props是否更新来判断这个组件是否更新。

以上就是对Component和PureComponent全部的分析。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值