在我们平时写一个类组件的时候,一般都会继承一个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全部的分析。