为何是setState,由于对于你们而言,大多数使用react的新手或者初学者,大多会直接接触到setState,并且这个方法也多是接触最多的操做方法。那么要想详细了解setState究竟在React中作了什么事情,就须要深刻了解一下。而在最新的React 16版本中,React的核心渲染框架时进行过一次升级的,由以前的React升级到了React Fiber。(PS:本文针对菜鸟、初级工程师而写,有错误不足之处,请各位大佬指出更正。感受太low,请绕道,谢谢。)前端
别着急,让我来慢慢给大家解答,在16版本以前,React使用的仍是旧版的渲染核心,它的渲染过程是一口气完成,怎么理解呢?就是会一次性遍历你全部的Dom节点,这个过程取决于你的应用的复杂程度。固然,这个过程通常比较快,可是也不排除在大型复杂应用中出现比较长的等待时间,这个时间是基于ms级别的。而做为一个前端工程师,性能优化是比较重要的一方面之一,你们都知道,浏览器是的渲染引擎是单线程的,这就意味着一个时间段以内只能完成一件事。当你的应用过于复杂时,用户操做变多,弊端就显示出来了:卡顿,未响应,甚至是页面崩溃...这就是为何React会升级到React Fiber,在未升级以前,渲染模式是这样的:react
假设你的结构是这样的 A组件 => B组件 => C/D/E组件 D组件 => F组件 未使用Fiber架构的渲染方式git
他的旧版渲染模式是这样的: github
在升级为Fiber以后,就如同游泳同样,每一个一段时间,都须要上岸呼吸一口气,因此渲染模式就变成更了如下状况: 算法
潜水员会每隔一段时间就上岸,看是否有更重要的事情要作。数组
加入fiber的react将组件更新分为两个阶段,Reconcile阶段和Commit阶段。浏览器
经过这个俩个阶段,你就会明白,为何以前会把componentWillMount、componentWillReviceProps和componentWillUpdate标记为不安全的生命周期函数了,由于在Reconcile阶段,被打断以后是从新进行的,就有可能形成对此的数据请求,对此渲染,形成没必要要的资源、性能浪费(这里有一个比较有意思饥饿问题,聪明的同窗应该已经猜出来了,react如今尚未公布解决方法哦)。安全
Fiber实际上是一个对象。在Fiber源码中,有这么一段描述性能优化
A Fiber is work on a Component that needs to be done or was done. There can be more than one per component.bash
Fiber就是经过对象记录组件上须要作或者已经完成的更新,一个组件能够对应多个Fiber。
接下来让咱们看看Fiber具体是什么样子的?既然是一个对象,就确定是{}模式。以下:
{
tag,
key,
elementType,
type,
stateNode,
return,
child,
sibling,
index,
ref,
pendingProps,
memoizedProps,
updateQueue,
memoizedState,
firstContextDependency,
mode,
effectTag,
nextEffect,
firstEffect,
lastEffect,
expirationTime,
childExpirationTime,
alternate,
actualDuration,
actualStartTime,
selfBaseDuration,
treeBaseDuration
}
复制代码
在render函数中建立的React Element树在第一次渲染的时候会建立一颗结构如出一辙的Fiber节点树。不一样的React Element类型对应不一样的Fiber节点类型。一个React Element的工做就由它对应的Fiber节点来负责。
Fiber的优先级以下:
高优先级会打断正在执行的低优先级任务先执行。
一个React Element能够对应不止一个Fiber,由于Fiber在更新的时候,会从原来的Fiber(current)克隆出一个新的Fiber(alternate)。两个Fiber diff出的变化(side effect)记录在alternate上。因此一个组件在更新时最多会有两个Fiber与其对应,在更新结束后alternate会取代以前的current的成为新的current节点。
这是fiber在目前版本v16.6.3所维护的全部属性,具体想要了解阅读源码请看这里。ReactFiber.js
在官方文档中,明确指出,要把state认做是不可变的,因此,如今更推崇的写法不是直接setState,而是经过setState的回调函数进行更改。
this.setState(() => {[key]: value});
复制代码
好,不说题外话了,让咱们进入今天的正题,setState。 你们写项目的时候,在index.js文件中,会引入两个文件,react,react-dom。setState在react文件是这样的:
在你的应用第一次渲染的时候,最主要的是关注react-dom的进行,前面说过updater是随后注入进去的,就是在react-dom加载的时候注入进去的。接下来,setState带你们去看看到底是什么?
直接来看setState队列,这里须要3个参数能够看到分别是实例对象,载荷和回调函数。在这里咱们先看在最开始生命4个变量分别是干什么用的,直接语义化就能猜出个大概来。
Q1:fiber经过get方法获取一些东西?
A1: 能够看到,源代码实现的方法,获再结合当前调用方法的上下文能够得知,当前的fiber获取到时当前实例上的一个_reactInternalFiber的值。这个值是什么,实际上是经过相应的一个set方法,将当前实例和workInProgress传入,并给赋值给当前实例的_reactInternalFiber属性。
Q2:currentTime获取当前的时间?
Q3:expirationTime获取到期时间?什么鬼?
Q4:update建立update队列?
A4: 这个阶段就是经过createUpdate来建立一个更新对象。
在进行了一系列不可描述的过程以后,终于能够进行接下来的操做了。
第一部分
- 首先判断是否是只有一个fiber,只有一个fiber的话就让q1等于这个值,而后q2克隆q1
- 若是是有俩个fiber,则q1等于当前实例的fiber.updateQueue,q2就等于alternate.updateQueue;
- 若是两个fiber都没有更新队列。则q1,q2都建立新的。
- 只有一个fiber有更新队列。克隆以建立一个新的。
- 俩个fiber都有更新队列。总之就是,q1和q2都须要有一个fiber。
第二部分
- 当q1与q2是相等时,一位置实际上只有一个fiber,将此fiber插入到更新队列;
- 若q1和q2有一个是非空队列,则两个对列都须要更新;
- 当q1和q2两个队列都是非空,因为结构共享,两个列表中的最后一次更新是相同的。所以,只需q1添加到更新队列便可;
- 最后将q2的lastUpdate指针更新。
最后一步,就是掉用scheduleWork()方法,来进行最后的更新。在此方法中会根据优先级进行分片式更新。
接下来,在commit阶段,一口气执行完毕。你的DOM就是最新的了。说了这么多,可能执行起来,就是短短的几十毫秒... 就好比下面
至此,setState整个过程算是完成了。
总结:这篇文章是鄙人第一次下手书写,有些地方可能表述不是很准确,可能有点啰嗦,可是我喜欢啊。俗话说万事开头难,可是过程也难啊,结果更难啊。对于代码也同样,要坚持下去,坚持下去你就得颈椎病了哦。本文有什么错误的地方,还烦请各路大神指出,鄙人是不会改滴,都会记在内心哒,上述是我对setState的理解,抛砖引玉,但愿帮助你们有方向的去了解react原理机制。