欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

react 源码之setState

程序员文章站 2022-03-20 19:26:33
今天看了react源码,仅以记录。 1:monorepo (react 的代码管理方式) 与multirepo 相对。 monorepo是单代码仓库, 是把所有相关项目都集中在一个代码仓库中,每个module独立发布,每个module都有自己的依赖项(package.json),能够作为独立的npm ......

今天看了react源码,仅以记录。

1:monorepo (react 的代码管理方式)

与multirepo 相对。 monorepo是单代码仓库, 是把所有相关项目都集中在一个代码仓库中,每个module独立发布,每个module都有自己的依赖项(package.json),能够作为独立的npm package发布,只是源码放在一起维护。

下图是典型monorepo目录图,react为例

react 源码之setState

2: setstate

✋ setstate  "react/src/reactbaseclasses.js"

 * @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');
};

可以看到,干了两件事情:第一:调用方法invariant(), 第二:调用 this.updater.enqueuesetstate

✋ invariant() "shared/invariant"  让我们先去看看第一个方法干嘛了!

export default function invariant(condition, format, a, b, c, d, e, f) {
  validateformat(format);

  if (!condition) { // 导出了一个方法,可以看出如果condition为false的话, 抛出错误
    let error;
    if (format === undefined) {
      error = new error(
        'minified exception occurred; use the non-minified dev environment ' +
          'for the full error message and additional helpful warnings.',
      );
    } else {
      const args = [a, b, c, d, e, f];
      let argindex = 0;
      error = new error(
        format.replace(/%s/g, function() {
          return args[argindex++];
        }),
      );
      error.name = 'invariant violation';
    }

    error.framestopop = 1; // we don't care about invariant's own frame
    throw error;
  }
}

得出,这个方法就是判断partialstate 的type, 不正确的话抛错误。 facebook工程师把这个抛错误封装了成了invariant函数,嗯,以后工作中可以这样做!

✋ this.updater.enqueuesetstate  让我们再去看看第二个是干嘛了!

首先,this.updater 什么鬼:

import reactnoopupdatequeue from './reactnoopupdatequeue';
/**
 * base class helpers for the updating state of a component.
 */
function component(props, context, updater) {//在这个文件中导出了两个构造函数component和purecomponent
  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; // 这里是this.updater, 这个标黄的 是default updater
}

✋ reactnoopupdatequeue  "react/src/reactnoopupdatequeue.js" 导出的一个对象,里面有好几个方法,其中一个就是setstate用到的this.updater.enqueuesetstate(this, partialstate, callback, 'setstate'):

enqueuesetstate: function(
    publicinstance, // 实例this
    partialstate, // 新的state
    callback, // 回调   
    callername,
  ) {
    warnnoop(publicinstance, 'setstate');
  },
// 啊 这个里面是default updater

✋  enqueuesetstate定义 "react-dom/src/server/reactpartialrenderer.js"    实际的updater从哪里来的呢?哎,我是暂时没找到,但是,我知道这个实际的updater肯定有enqueuesetstate方法,那我就全局搜索一下,找到enqueuesetstate的定义:

   let updater = {  //这里是updater对象,里面各种方法
      ismounted: function(publicinstance) {
        return false;
      },
      enqueueforceupdate: function(publicinstance) {
        if (queue === null) {
          warnnoop(publicinstance, 'forceupdate');
          return null;
        }
      },
      enqueuereplacestate: function(publicinstance, completestate) {
        replace = true;
        queue = [completestate];
      },
      enqueuesetstate: function(publicinstance, currentpartialstate) {
        if (queue === null) { // 这是一个错误情况,下面代码中warnnoop()方法就是在dev环境中给出空操作警告的
          warnnoop(publicinstance, 'setstate');
          return null;
        }
        queue.push(currentpartialstate); // 把现在要更新的state  push到了一个queue中
      },
    };
     // 接下来代码解决了我上面找不到实际updater的疑问!
    let inst;
    if (shouldconstruct(component)) {
      inst = new component(element.props, publiccontext, updater); // 在new的时候,就把上面真实的updater传进去啦!!!
.......后面还有好多,不过与我这一期无关了.

✋  queue 这是react提升性能的关键。并不是每次调用setstate react都立马去更新了,而是每次调用setstate, react只是push到了待更新的queue中! 下面是对这个queue的处理!

      if (queue.length) {
        let oldqueue = queue;
        let oldreplace = replace;
        queue = null;
        replace = false;

        if (oldreplace && oldqueue.length === 1) { // 队列里面,只有一个,直接更换。
          inst.state = oldqueue[0];
        } else {  // 队列里面有好几个,先进行合并,再更新
          let nextstate = oldreplace ? oldqueue[0] : inst.state;
          let dontmutate = true;
          for (let i = oldreplace ? 1 : 0; i < oldqueue.length; i++) {
            let partial = oldqueue[i];
            let partialstate =
              typeof partial === 'function'
                ? partial.call(inst, nextstate, element.props, publiccontext)
                : partial;
            if (partialstate != null) {  // 这里合并啦,新的是nextstate
              if (dontmutate) {
                dontmutate = false;
                nextstate = object.assign({}, nextstate, partialstate);
              } else {
                object.assign(nextstate, partialstate);
              }
            }
          }   
          inst.state = nextstate;  //最后赋值给实例
        }
      } else {
        queue = null;
      }