概述
- Component 就是组件, 这个概念依托于最直观的在react上的一个表现,那就是
React.Component
- 我们写的组件大都是继承于
React.Component
这个baseClass 而写的类 - 这个类代表着我们使用 react 去实现的一个组件
- 那么在react当中不仅仅只有
Component
这一个baseClass,还有另外一个叫PureComponent
PureComponent
跟Component
唯一区别- 提供了简便的
shouldComponentUpdate
的一个实现 - 保证组件在 props 没有任何变化的情况下减少不必要的更新
- 提供了简便的
源码分析
1 ) API 位置定位
-
在入口文件 React.js里面,可看到
jsimport {Component, PureComponent} from './ReactBaseClasses';
-
之后,我们定位到 ReactBaseClasses.js 里面
ReactBaseClasses.js 源码
js
/**
* 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};
2 )源码分析
2.1 Component
-
从上面看到,Component 是一个function,它是一个使用function去进行类的声明的一个方式
-
它接收三个参数,一个是
props
,一个是context
,还有一个是updater
-
关于 props 和 context 可以发现, 在这里面进行赋值
this.props = props
this.context = context
- 就是我们在组件内部去使用的时候,可以直接去用
-
然后再声明了一个 refs,它是一个 emptyObject
- 参考 stringRef, 最终会把获取的节点的实例挂载在 this.refs 上面
- 就是我们 stringRef 使用的 key 挂载在上面
-
关于 updater 出场率并不高,先跳过
-
往下看,找到
Component.prototype.setState
jsComponent.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'); };
- 这个就是我们使用的最多的一个API, 它是用来更新我们组件的状态
- 它接收的两个参数
* 一个是partialState
, 就是我们要更新的一个新的state,可以是一个对象,也可以是一个方法
* 第二个是callback
, 那么callback就是在我们state真正的更新完之后才会执行这个callback - 这个方法里面第一段代码是一个提醒
- 就是它判断我们的对象是不是object或者是function, 或者是不等于null
- 如果都不满足,它就会给出一个错误提醒
* 这个其实不太重要代码
- 下面一段才是重点
this.updater.enqueueSetState
- 调用 setState,其实在 Component 对象里面, 什么事情都没有做
- 它只是调用了初始化Component时, 传入的这个update对象上面的一个 enqueueSetState 方法
- 这个方法是在 react-dom 里面去实现的,跟react的代码是没有任何关系的
- 这样做的原因是,不同的平台,比如说react-dom和react-native
- 它们用的react的核心是一模一样的,也就是说,Component 这个API是一模一样的
- 但是,具体的涉及到更新state之后如何进行渲染,而这个渲染的流程在跟平台有关的
- 所以这里是一种类似于适配器或外观模式的一种设计模式,我更倾向于它是一个适配器
- react-dom平台跟react-native平台,它们的实现渲染的方式肯定是不一样的
- updater 它作为一个参数,让不同的平台传参进来,定制它自己的一个实现方式
- 所以,这就是为什么要封装传参
-
接下来,进入到
Component.prototype.forceUpdate
jsComponent.prototype.forceUpdate = function(callback) { this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); };
- 这个跟 setState 其实是一样的,它是调用了
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
这个方法 - 这个API不是很常用,它是强制 react Componet 更新一遍,即便你的state没有进行更新
- 这个跟 setState 其实是一样的,它是调用了
-
接下来,在 DEV 环境的判断下,有两个即将被废弃的API
- 一个是
isMounted
- 另外一个是
replaceState
- 一个是
-
到这里为止,可以看到 Component 的定义已经结束了, 它只有这么一点东西
-
这个 Component 的定义,只是用来帮助我们去承载一些信息的
-
没有任何的其他含义,也没有任何关于生命周期相关的一些方法, 就是这么简洁
2.2 PureComponent
-
其实可以认为它是继承于 Component
-
其实也没多少可以继承的东西,它们接收的东西也是一模一样的
js/** * 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};
- 使用这个 ComponentDummy 实现了一个类似于简单的继承的方式
- 它 new了一个 ComponentDummy,它是一个空的类
- 它的 constructor 指向 PureComponent 自己, 这其实就是一个实继继承的过程
- 之后把 Componen.prototype 上面的属性拷贝到 pureComponentPrototype 上面
- 唯一的一个区别是
pureComponentPrototype.isPureReactComponent = true;
- 通过这个属性来标识,继承之这个类的组件,它是一个 PureComponent
- 在后续更新的过程中,react-dom 主动的去判断它是不是一个 PureComponent
- 然后去根据 props 是否更新来判断这个组件是否需要更新