React中最重要的就是组件,写的更多的组件都是继承至 React.Component 。大部分同学可能都会认为 Component 这个base class 给我们提供了各种各样的功能。他帮助我们去运行了这个 render function 。然后最终把我们写在里面的 dom 标签或者子组件之类的渲染出来。渲染到我们的浏览器里面变成想要的页面的一个形式。在真正的看源码之前我也是这么认为的。但是看了源码之后发现他颠覆了我对他的一个认知。
在 React 当中不仅仅只有 Component 这一个 base class 。还有另外一个叫做 PureComponent 。这个跟 Component 他唯一的区别就是他提供了简便的 shouldComponentUpdate 的一个实现。他保证我们的组件在 props 没有任何变化的情况下能够减少必要的更新。
在源码 React.js 里面找到 import {Component, PureComponent} from './ReactBaseClasses'; 得知 Component 来自于 ReactBaseClasses.js 。 打开 ReactBaseClasses.js 。 找到 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 是一个 function 去声明一个类的方式。他接收三个参数,一个是 props, 一个是 context , 一个是 updater 。props 和 context 应该是有用到过的。在代码里面我们可以看到 this.props = props , this.context = context 。在组件内部使用的时候我们就是可以直接去用了。然后继续声明了一个 this.refs = emptyObject 。 使用过 React 当中的 string ref 的同学,就知道这个东西的意义是什么。他最终会把我们想要获取的那个节点的实例挂载在 string ref 上。最后一个 update 就比较好奇了,因为我们并没有在 React.Component 当中使用到过这个对象。那么这个对象具体又有什么意义呢,我们接下去往下看。
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');
};

我们看到 Component 的 prototype 上面有个 isReactComponent ,他等于一个空对象。这个东西好像没有什么特别的用处,就先略过。

然后下面有个原型上的方法叫做 setState 。那么这个就是我们在 React 当中使用最多的一个api了。他是用来更新我们组件的状态的。那么他接收两个参数,一个是partialState,他就是我们要更新的 state ,可以是个对象,也可以是个方法。在后续的 react 版本中,更推荐使用方法。第二个是个 callback。那么 callback 就是在 state 真正更新完之后他会执行这个 callback 。这个方法里面可以看到 invariant 。 invariant这一段代码他就是一个提醒。他判断一下 partialState 是对象,方法还是 null 。如果都不满足,就会有后续的一个字符串提醒。
这个就是一个不重要的代码。那么重要的是什么呢,重要的是 this.updater.enqueueSetState 。也就是说我们调用 setState ,其实就是调用 enqueueSetState。这个 enqueueSetState 就是我们初始化 Component 的时候传进来的 enqueueSetState 这个方法。这个方式是在 React dom 里面去实现的,跟 React 是没有关系的。为什么要这么去做呢,因为不同的平台,比如 React dom 和 React native 。他们用的核心一模一样的。也就是 Component api 是一样的。但是具体到更新 state 。更新了 state 设计到渲染,那么这个渲染流程是跟平台有关的。react dom 平台跟 react native 平台他实现的方式肯定不一样的。所以这部分作为一个参数让不同的平台传入进来去定制他自己的一个实现方式。这就是 enqueueSetState 要在 setState 里面调用的原因。
接下来原型上还有个方法是 forceUpdate 。这个跟 setState 一样的。也是调用了 updater 里面的一个方法。叫做 enqueueForceUpdate。这个 api 也不是很常用,就是强制 react 去更新一遍,即便你的 state 没有进行一个更新。
/**
* 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]);
}
}
}
接下来看到 isMounted 和 replaceState 。这是即将被废弃的两个 api 。这两个 api 因为本身就没有怎么用到,也没有什么特别大的用处。下个版本就没有,所以这段就略过。
到这里我们可以看到 Component 的定义已经结束了。所以这个 base class 只有这么一点东西,没有任何其他含义,也没有任何关于生命周期的相关方法。看到这里肯定非常迷糊,这是没有办法的事情,因为开始看源码的时候我也是抱着 React.Component 也是非常复杂的,他帮我们实现了各种各样的逻辑。但事实上直到后续看 React dom 如何去创建更新以及实现这整个的流程,我才发现这个 Component 只是用来帮助我们去承载一些信息的。
接下来我们看另外一个 base class ,也就是 PureComponent
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;

那么 PureComponent 我们可以认为他是继承与 Componnet 。他们两个也没有什么可继承的东西,因为他们两接收的东西是一模一样的,都是 props, context, updater 。这里用 ComponentDummy 去实现了一个简单的类似于继承的方式。 PureComponent.prototype 去 new 了一个 ComponentDummy 。 ComponentDummy 是一个空类。 将 Component 的原型赋给了 ComponentDummy 的原型。然后又将 ComponentDummy 的原型 赋给了PureComponent 。pureComponentPrototype 的构造函数指向了 pureComponent 。实际这就是一个实现继承的过程。其实就是一模一样。唯一的区别就是上面加了一个 isPureReactComponent 。他通过这么一个属性来标识继承自这个类的组件,他是一个 PureReactComponent。然后在后续更新的过程当中。 React dom 他会主动的去判断他是不是一个 PureComponent 。然后根据 props 是否更新来判断这个这个组件是否需要更新。

React源码 React.Component的更多相关文章

  1. React源码 React ref

    ref 的功能,在 react 当中.我们写了一个组件,在这个时候,我们的 render function 里面我们会渲染一系列的子组件或者 dom 节点,有时候我们会希望有这样的需求,就是我们要获取 ...

  2. React源码 React.Children

    children是什么意思呢?就是我们拿到组件内部的props的时候,有props.children这么一个属性,大部分情况下,我们直接把 props.children 渲染到 JSX 里面就可以了. ...

  3. 读react源码准备

    git源码地址:https://github.com/facebook/react react 里面就是 react源码 react里面的react文件夹就是react源码,react源码非常的少,总 ...

  4. React源码剖析系列 - 生命周期的管理艺术

    目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理.本系列文章希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期(C ...

  5. React源码解析:ReactElement

    ReactElement算是React源码中比较简单的部分了,直接看源码: var ReactElement = function(type, key, ref, self, source, owne ...

  6. react 源码之setState

    今天看了react源码,仅以记录. 1:monorepo (react 的代码管理方式) 与multirepo 相对. monorepo是单代码仓库, 是把所有相关项目都集中在一个代码仓库中,每个mo ...

  7. React 源码剖析系列 - 不可思议的 react diff

      简单点的重复利用已有的dom和其他REACT性能快的原理. key的作用和虚拟节点 目前,前端领域中 React 势头正盛,使用者众多却少有能够深入剖析内部实现机制和原理. 本系列文章希望通过剖析 ...

  8. React 源码剖析系列 - 生命周期的管理艺术

    目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理. 本系列文章 希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期 ...

  9. React躬行记(16)——React源码分析

    React可大致分为三部分:Core.Reconciler和Renderer,在阅读源码之前,首先需要搭建测试环境,为了方便起见,本文直接采用了网友搭建好的环境,React版本是16.8.6,与最新版 ...

随机推荐

  1. Gamma阶段事后分析

    设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件要解决的是安卓游戏的自动化异常检测问题,定义的足够清楚,对于典型用户的描述和典型场景的描述也足 ...

  2. docker 挂载主机目录 -v 和 --mount区别

    使用-v  时,如果宿主机上没有这个文件,也会自动创建, 但是如果使用--mount时,宿主机中没有这个文件会报错找不到这个文件,并创建失败

  3. 025 Linux基础入门-----历史、简介、版本、安装

    1.linux历史 Linux最初是由芬兰赫尔辛基大学学生Linus Torvalds由于自己不满意教学中使用的MINIX操作系统, 所以在1990年底由于个人爱好设计出了LINUX系统核心.后来发布 ...

  4. Delphi阿里云邮件推送【支持单一发信、邮件批量发送和获取指定条件下的发送数据】

    作者QQ:(648437169) 点击下载➨Delphi阿里云邮件推送               阿里云api文档 [Delphi阿里云邮件推送]支持SingleSendMail(单一发信接口). ...

  5. Django 模板语言 变量名称

    Django 模板语言 变量名称 模板语言中已变量形式显示 # view 文件内 def func(request): return render(request,"index.html&q ...

  6. spring接口文档注解:@ApiOperation

    @ApiOperation不是spring自带的注解是swagger里的 com.wordnik.swagger.annotations.ApiOperation; @ApiOperation和@Ap ...

  7. 解决老大难疑惑:指针 vs 引用

    ▶疑问描述 1.  引用reference的本质: 常指针 ——> 什么时候用指针?= 就按Java中的引用变量那样用? ——> 什么时候用引用?  ①函数的入参/返回值时   ②T&am ...

  8. Docker 多终端登录

    版权声明:starRTC免费im直播会议一对一视频,by elesos.com & starRTC.com https://blog.csdn.net/elesos/article/detai ...

  9. Python面向对象封装案例

    01. 封装 封装 是面向对象编程的一大特点 面向对象编程的 第一步 —— 将 属性 和 方法 封装 到一个抽象的 类 中 外界 使用 类 创建 对象,然后 让对象调用方法 对象方法的细节 都被 封装 ...

  10. 关于springMVC中的路径问题

    相对路径中,我们最后想要的到的是绝对路径,而绝对路径=参照路径+相对路径: 相对路径往往都知道,只需要区分参照路径即可:对于前台和后台,参照路径不太相同: 什么是前台,后台路径: 前台路径: 出现在 ...