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. Leetcode 1254. 统计封闭岛屿的数目

    题目: 有一个二维矩阵 grid ,每个位置要么是陆地(记号为 0 )要么是水域(记号为 1 ). 我们从一块陆地出发,每次可以往上下左右 4 个方向相邻区域走,能走到的所有陆地区域,我们将其称为一座 ...

  2. ASP.NET Core WebApi基于JWT实现接口授权验证

    一.ASP.Net Core WebApi JWT课程前言 我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再 ...

  3. Maven设置MAVEN_OPTS环境变量

    原文地址:https://blog.csdn.net/porsche_gt3rs/article/details/78787491 一 原因: 运行mvn命令实际是执行java命令,既然是运行java ...

  4. 逻辑运算符/三元运算符/Scanner

    逻辑运算符 &(并且) , |(或者) , !(非) , ^(异或) , &&(短路与) , ||(短路或) 注意事项: a:逻辑运算符一般用于连接boolean类型的表达式或 ...

  5. python读取word中的段落、表、图+++++++++++Doc转换Docx

    读取文本.图.表.解压信息 import docx import zipfile import os import shutil '''读取word中的文本''' def gettxt(): file ...

  6. python入门之名称空间的查找顺序

    名称空间的查找顺序: 局部:局部 > 全局 > 内置 全局:全局 > 内置 # 内置再找不到就报错 # 函数内部使用的名字,在定义阶段已经规定死了,与你的调用位置无关 x = 111 ...

  7. cocos2d设置窗口标题

    //窗口标题 #ifdef WIN32 CCEGLView* pGlView=CCDirector::sharedDirector()->getOpenGLView(); if (pGlView ...

  8. c# mvc webapi的put报405错误

    程序在本机调试可正常修改,本机是iis11 放到服务器上,报错了:405.服务器iis7.0 返回的错误页面: <!DOCTYPE html PUBLIC "-//W3C//DTD X ...

  9. ansible模块command、shell、raw、script

    简介 环境: ansible端: ip:192.168.100.129 hostname:node1.lansgg.com client端: ip:192.168.100.131 hostname:v ...

  10. python 面向对象编程、获取对象信息

    面向对象与面向过程 参考链接:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0 ...