react 源码之setState
今天看了react源码,仅以记录。
1:monorepo (react 的代码管理方式)
与multirepo 相对。 monorepo是单代码仓库, 是把所有相关项目都集中在一个代码仓库中,每个module独立发布,每个module都有自己的依赖项(package.json
),能够作为独立的npm package发布,只是源码放在一起维护。
下图是典型monorepo目录图,react为例
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; }
上一篇: vue中路由按需加载的几种方式