React16源码: Component与PureComponent源码实现

概述

  • Component 就是组件, 这个概念依托于最直观的在react上的一个表现,那就是 React.Component
  • 我们写的组件大都是继承于 React.Component 这个baseClass 而写的类
  • 这个类代表着我们使用 react 去实现的一个组件
  • 那么在react当中不仅仅只有 Component 这一个baseClass,还有另外一个叫 PureComponent
  • PureComponentComponent 唯一区别
    • 提供了简便的 shouldComponentUpdate 的一个实现
    • 保证组件在 props 没有任何变化的情况下减少不必要的更新

源码分析

1 ) API 位置定位

  • 在入口文件 React.js里面,可看到
    import {Component, PureComponent} from './ReactBaseClasses';
    
  • 之后,我们定位到 ReactBaseClasses.js 里面

ReactBaseClasses.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

    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');
    };
    
    • 这个就是我们使用的最多的一个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

    Component.prototype.forceUpdate = function(callback) {this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
    };
    
    • 这个跟 setState 其实是一样的,它是调用了 this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); 这个方法
    • 这个API不是很常用,它是强制 react Componet 更新一遍,即便你的state没有进行更新
  • 接下来,在 DEV 环境的判断下,有两个即将被废弃的API

    • 一个是 isMounted
    • 另外一个是 replaceState
  • 到这里为止,可以看到 Component 的定义已经结束了, 它只有这么一点东西

  • 这个 Component 的定义,只是用来帮助我们去承载一些信息的

  • 没有任何的其他含义,也没有任何关于生命周期相关的一些方法, 就是这么简洁

2.2 PureComponent

  • 其实可以认为它是继承于 Component

  • 其实也没多少可以继承的东西,它们接收的东西也是一模一样的

    /*** 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 是否更新来判断这个组件是否需要更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/588609.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【web安全】登录界面渗透的思路总结

前言 小菜作者的总结。 如果大家知道其他的安全问题,欢迎大家补充赐教。我们一起完善登录页面的渗透思路。 (如果大家有好的博客讲解对应的漏洞,欢迎分享~) sql注入 登录页面是需要与数据库打交道的。是需要带入数据库查询的…

JVM之jinfo虚拟机配置信息工具

jinfo虚拟机配置信息工具 1、jinfo jinfo(Configuration Info for Java)的作用是实时地查看和调整虚拟机的各项参数。 使用jps -v 可以查看虚拟机启动时显示指定的参数列表,但是如果想知道未被显示指定的参数的系统默认值,除 …

GCP 创建1个windows vm 并连接

有时需要临时使用1台windows 的机器 创建windows vm 既然是临时 直接用gcloud command gcloud compute instances create instance-windows \--zoneeurope-west2-c \--machine-typen2d-standard-4 \--boot-disk-size100GB \--image-projectwindows-cloud \--imagewindows-se…

Rust学习笔记001:HELLOW WORLD + Cargo

Rust介绍 Rust(中文称为“锈”)是一种由Mozilla开发的系统编程语言,它着力于提供安全性、并发性和实用性。Rust的设计目标是消除程序出现的内存安全性问题,如空指针引用、数据竞争等。它通过在编译时进行严格的所有权和借用检查来…

Vue(三):Vue 生命周期与工程化开发

2023 的最后一篇博客,祝大家元旦快乐,新的一年一起共勉! 06. Vue 生命周期 6.1 基本介绍 生命周期就是一个 Vue 示例从 创建 到 销毁 的整个过程,创建、挂载、更新、销毁 有一些请求是必须在某个阶段完成之后或者某个阶段之前执行…

10TB海量JSON数据从OSS迁移至MaxCompute

前提条件 开通MaxCompute。 在DataWorks上完成创建业务流程,本例使用DataWorks简单模式。详情请参见创建业务流程。 将JSON文件重命名为后缀为.txt的文件,并上传至OSS。本文中OSS Bucket地域为华东2(上海)。示例文件如下。 {&qu…

zabbix通过自动发现-配置监控项、触发器(小白教程)

自动发现配置参考链接(不小白,不友好) zabbix-get介绍 1配置 zabbix server:版本7(不影响),IP地址:192.168.0.60zabbix agent:版本agent1(不影响)&#xff…

Megatron-LM源码系列(六):Distributed-Optimizer分布式优化器实现Part1

1. 使用说明 在megatron中指定--use-distributed-optimizer就能开启分布式优化器, 参数定义在megatron/arguments.py中。分布式优化器的思路是将训练中的优化器状态均匀地分布到不同数据并行的rank结点上,相当于开启ZERO-1的训练。 group.add_argument(--use-distr…

SpringIOC之ClassPathXmlApplicationContext

博主介绍:✌全网粉丝5W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验…

PostgreSQL16.1(Windows版本)

1、卸载原有的PostgreSQL   点击Next即可。  点击OK即可。 卸载完成。 2、安装 (1) 前两部直接Next,第二部可以换成自己想要安装的路径。 (2) 直接点击Next。…

云原生Kubernetes系列 | Job和CronJob使用

云原生Kubernetes系列 | Job和CronJob使用 1. Job和CronJob的作用2. Job3. CronJob1. Job和CronJob的作用 Kubernetes官网-Job和CronJob使用手册 传统运行的pod,比如Deployment管理的Pod,或手工管理的Pod,只要创建好Pod该Pod会一致运行下去。Pod里面运行的是一个daemon守护进…

雪花算法(Snowflake)介绍和Java实现

1、雪花算法介绍 (1) 雪花算法(SnowFlake)是分布式微服务下生成全局唯一ID,并且可以做到去中心化的常用算法,最早是Twitter公司在其内部的分布式环境下生成ID的方式。 雪花算法的名字可以这么理解,世界上没有两片完全相同的雪花,…

前端学习笔记 2:Vue

前端学习笔记 2:Vue Vue 是一个目前最流行的前端响应式框架,关于它的简单介绍可以观看这里。 1.快速入门 Vue 的官网提供一个快速上手指南,有多种方式可以安装和使用 Vue,这里展示一个最简单的方式——直接导入官方提供的在线的…

Linux高并发服务器开发之网络编程

Linux网络编程 网络基础TCP和socketTCP状态转移和IO多路复用反应堆模型和线程池模型libeventwebserver项目 网络基础 TCP和socket TCP状态转移和IO多路复用 反应堆模型和线程池模型 libevent webserver项目

Select缺点及代码示例

一、Select缺点 二、服务器端 #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/select.h>int main() {// 创建socketint lfd socket(PF_INET, SOCK_STREAM, 0)…

006、函数

1. 一个小技巧 在前面文章中&#xff0c;我们提到&#xff0c;在黑窗口中输入 code . 命令可以快速在 Visual Studio Code 中打开新建的项目&#xff0c;这个是你刚刚新建了项目&#xff0c;并且黑窗口正好是打开的情况下。 如果是之前创建的项目&#xff0c;用上面的方法就会有…

git(安装,常用命令,分支操作,gitee,IDEA集成git,IDEA集成gitee,IDEA集成github,远程仓库操作)

文章目录 1. Git概述1.1 何为版本控制1.2 为什么需要版本控制1.3 版本控制工具1.4 Git简史1.5 Git工作机制1.6 Git和代码托管中心 2. Git安装3. Git常用命令3.1 设置用户签名3.1.1 说明3.1.2 语法3.1.3 案例实操 3.2 初始化本地库3.2.1 基本语法3.2.2 案例实操3.2.3 结果查看 3…

【Java】log4j和slf4j区别

log4j&#xff1a;Apache Software Foundation 开源 slf4j&#xff1a;不支持日志滚动等高级功能 在开源库或内部库中使用 SLF4J&#xff0c;将使其独立于任何特定的日志记录实现&#xff0c;这意味着无需为多个库管理多个日志记录配置&#xff0c;您的客户端将会很需要这一点…

【k8s】deamonset文件和说明

目录 deamonset的相关命令 deamonset的定义 deamonset的使用场景 deamonset的例子 deamonset字段说明 serviceAccountName DaemonSet的结构及其各个部分的作用 deamonset的相关命令 #查看<name-space>空间内有哪些deamonset kubectl get DaemonSet -n <na…

Django 学习教程- Django 入门案例

Django学习教程系列 Django学习教程-介绍与安装 前言 本教程是为 Django 5.0 编写的&#xff0c;它支持 Python 3.10 至以上。如果 Django 版本不匹配&#xff0c;可以参考教程 使用右下角的版本切换器来获取你的 Django 版本 &#xff0c;或将 Django 更新到最新版本。如果…