React16源码: Component与PureComponent源码实现

这篇具有很好参考价值的文章主要介绍了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

  • 其实也没多少可以继承的东西,它们接收的东西也是一模一样的文章来源地址https://www.toymoban.com/news/detail-770377.html

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

到了这里,关于React16源码: Component与PureComponent源码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React16源码: React中的beginWork的源码实现

    beginWork 1 )概述 在 renderRoot 之后,要对我们的 Fiber 树每一个节点进行对应的更新 更新节点的一个入口方法,就是 beginWork 这个入口方法会有帮助我们去优化整棵树的更新过程 react 它的节点其实是非常多的,如果每一次子节点的一个更新 就需要每一个节点都执行一遍更新的话

    2024年01月20日
    浏览(45)
  • React16源码: React中的unwindWork的源码实现

    unwindWork 1 )概述 在 renderRoot 的 throw Exception 里面, 对于被捕获到错误的组件进行了一些处理 并且向上去寻找能够处理这些异常的组件,比如说 class component 里面具有 getDerivedStateFromError 或者 componentDidCatch 这样的生命周期方法 这个class component 就代表它可以处理它的子树当中渲

    2024年01月25日
    浏览(36)
  • React16源码: React中的performWork的源码实现

    performWork 1 )概述 performWork 涉及到在调度完成,或者同步任务进来之后整个 root 节点链条如何更新 怎么更新一棵 Fiber 树,它的每一个节点是如何被遍历到,以及如何进行更新操作 A. 在执行 performWork 时候,是否有 deadline 的区分 deadline 是通过 reactschedule 它的一个时间片,更新

    2024年01月17日
    浏览(39)
  • React16源码: React中的updateHostRoot的源码实现

    HostRoot 的更新 1 )概述 HostRoot 是一个比较特殊的节点, 因为在一个react应用当中 它只会有一个 HostRoot , 它对应的 Fiber 对象是我们的 RootFiber 对象 重点在于它的更新过程 2 )源码 定位到 packages/react-reconciler/src/ReactFiberBeginWork.js#L612 HostRoot 创建更新的过程就是在 ReactFiberReconcile

    2024年01月22日
    浏览(43)
  • React16源码: React中的updateClassComponent的源码实现

    ClassComponent 的更新 1 ) 概述 在 react 中 class component,是一个非常重要的角色 它承担了 react 中 更新整个应用的API setState forceUpdate 在react当中,只有更新了state之后,整个应用才会重新进行渲染 在 class component 中, 它的逻辑相对复杂 2 )源码 在 packages/react-reconciler/src/ReactFiberB

    2024年01月21日
    浏览(38)
  • React16源码: React中的renderRoot的源码实现

    renderRoot 1 )概述 renderRoot 是一个非常复杂的方法 这个方法里处理很多各种各样的逻辑, 它主要的工作内容是什么? A. 它调用 workLoop 进行循环单元更新 遍历整个 Fiber Tree,把每一个组件或者 dom 节点对应的 Fiber 节点拿出来单一的进行更新,这是一个循环的操作 把整棵 Fiber T

    2024年01月17日
    浏览(42)
  • React16源码: React中的reconcileChildren的源码实现

    reconcileChildren 1 )概述 在更新了一个节点之后,拿到它的props.children 要根据这个children里面的 ReactElement 来去创建子树的所有的 fiber 对象 要根据 props.children 来生成 fiber 子树,然后判断 fiber 对象它是否是可以复用的 因为我们在第一次渲染的时候,就已经渲染了整个 fiber 子树

    2024年01月20日
    浏览(40)
  • React16源码: React中调度之requestWork的源码实现

    requestWork 1 )概述 在 scheduleWork 中,找到了创建更新的fiber对应的root节点 然后对它进行了一些操作之后,调用了 requestWork ,开始请求工作 在 requestWork 里面它会做哪些东西呢? 首先我们要把这个root节点加入到调度队列当中 然后判断是否是批量更新 最后再根据 expirationTime 的

    2024年01月17日
    浏览(40)
  • React16源码: React中调度之batchedUpdates的源码实现

    batchedUpdates 1 )概述 在 requestWork 在中间, 会判断一个 isBatchingUpdates 做一些特定的操作 batchedUpdates 是一个批量更新的操作, 什么是批量更新呢?先来看一个例子 2 )示例 index.js demos/batchedUpdates 在上述 countNumber 中定义了更改 state的方法 当点击 按钮时,里面的 num 会更改,回调函

    2024年01月17日
    浏览(40)
  • React16源码: React中的setState和forceUpdate源码实现

    setState 和 forceUpdate 1 ) 概述 通过 class component 内部的 setState ,以及 forceUpdate 去更新一个组件的过程 在react的应用当中,我们只有 ReactDOM.render setState ,以及 forceUpdate 这几种种方式去更新react的应用是合理的,其他没有什么特别常用的方式去更新了 而且react官方推荐的也是用

    2024年01月25日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包