React浅谈setState

栏目: IOS · Android · 发布时间: 7年前

内容简介:为什么是setState,因为对于大家而言,大多数使用react的新手或者初学者,大多会直接接触到setState,而且这个方法也可能是接触最多的操作方法。那么要想详细了解setState究竟在React中做了什么事情,就需要深入了解一下。而在最新的React 16版本中,React的核心渲染框架时进行过一次升级的,由之前的React升级到了React Fiber。(PS:本文针对菜鸟、初级工程师而写,有错误不足之处,请各位大佬指出更正。感觉太low,请绕道,谢谢。)别着急,让我来慢慢给你们解答,在16版

为什么是setState,因为对于大家而言,大多数使用react的新手或者初学者,大多会直接接触到setState,而且这个方法也可能是接触最多的操作方法。那么要想详细了解setState究竟在React中做了什么事情,就需要深入了解一下。而在最新的React 16版本中,React的核心渲染框架时进行过一次升级的,由之前的React升级到了React Fiber。(PS:本文针对菜鸟、初级工程师而写,有错误不足之处,请各位大佬指出更正。感觉太low,请绕道,谢谢。)

  • 为什么会升级?
  • 为什么了解Fiber?

别着急,让我来慢慢给你们解答,在16版本之前,React使用的还是旧版的渲染核心,它的渲染过程是一口气完成,怎么理解呢?就是会一次性遍历你所有的Dom节点,这个过程取决于你的应用的复杂程度。当然,这个过程一般比较快,但是也不排除在大型复杂应用中出现比较长的等待时间,这个时间是基于ms级别的。而作为一个前端工程师,性能优化是比较重要的一方面之一,大家都知道,浏览器是的渲染引擎是单线程的,这就意味着一个时间段之内只能完成一件事。当你的应用过于复杂时,用户操作变多,弊端就显示出来了:卡顿,未响应,甚至是页面崩溃...这就是为什么React会升级到React Fiber,在未升级之前,渲染模式是这样的:

假设你的结构是这样的 A组件 => B组件 => C/D/E组件 D组件 => F组件 未使用Fiber架构的渲染方式

他的旧版渲染模式是这样的:

React浅谈setState

以render()函数为分界线。从顶层组件开始,一直往下,直至最底层子组件。然后再往上。组件update阶段同理。一直执行,直到完成,这个过程完全不理你。(我喜欢叫狗不理阶段)

在升级为Fiber之后,就如同游泳一样,每个一段时间,都需要上岸呼吸一口气,所以渲染模式就变成更了以下情况:

React浅谈setState

潜水员会每隔一段时间就上岸,看是否有更重要的事情要做。

加入fiber的react将组件更新分为两个阶段,Reconcile阶段和Commit阶段。

  • Reconcile阶段,在这个阶段内,React通过diff算法,判断哪些组价需要更新,经需要更新的组件打上tag(标记),再将所有需要更新的组件添加到一个数组中,等待或者执行更新任务。注意:这个阶段是可以被打断的,也就是说在这个阶段内,react检测到有用户操作行为,或者是其他的一些事情都会打断,在事件执行完毕之后在重新将进行此阶段,是重新进行。
  • Commit阶段,这个阶段是根据Reconcile阶段生成的更新的数组,遍历更新DOM,这个阶段是一次性执行完毕的,并且是不会被打断的。

通过这个俩个阶段,你就会明白,为什么之前会把componentWillMount、componentWillReviceProps和componentWillUpdate标记为不安全的生命周期函数了,因为在Reconcile阶段,被打断之后是重新进行的,就有可能造成对此的数据请求,对此渲染,造成不必要的资源、性能浪费(这里有一个比较有意思饥饿问题,聪明的同学应该已经猜出来了,react现在还没有公布解决方法哦)。

Fiber具体是什么样的?

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.

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浅谈setState

高优先级会打断正在执行的低优先级任务先执行。

一个React Element可以对应不止一个Fiber,因为Fiber在更新的时候,会从原来的Fiber(current)克隆出一个新的Fiber(alternate)。两个Fiber diff出的变化(side effect)记录在alternate上。所以一个组件在更新时最多会有两个Fiber与其对应,在更新结束后alternate会取代之前的current的成为新的current节点。

这是fiber在目前版本v16.6.3所维护的所有属性,具体想要了解阅读源码请看这里。 ReactFiber.js

setState

在官方文档中,明确指出,要把state认作是不可变的,所以,现在更推崇的写法不是直接setState,而是通过setState的回调函数进行更改。

this.setState(() => {[key]: value});
复制代码

好,不说题外话了,让我们进入今天的正题,setState。 大家写项目的时候,在index.js文件中,会引入两个文件,react,react-dom。setState在react文件是这样的:

React浅谈setState

熟不熟悉?Conmponent类,在这里面我们可以看看干了什么事情,接受props,context和updater,注意我拿红线标出来的部分,短路运算,再看看注释,这个updater是随后注入进去的。先不管是什么时候注入进去的,让我们接着往下看,setState肯定会触发更新,那我们就沿着this.updater往下走,去寻找ReactNoopUpdateQueue(react空操作更新队列),很多人会犯嘀咕,都空操作了还要更新什么?耐心点,这里的确是不进行任何更新操作,只是验证一个数据格式,和检验旧版V8引擎的一些错误,并抛出来。

React浅谈setState

这是什么?setState?干了什么?参数校验,如果通过就执行下面的方法,this指的当前实例。

React浅谈setState

在enqueueSetState方法中,也是实例验证。验证实例是否mounted。

在你的应用第一次渲染的时候,最主要的是关注react-dom的进行,前面说过updater是随后注入进去的,就是在react-dom加载的时候注入进去的。接下来,setState带大家去看看究竟是什么?

React浅谈setState

直接来看setState队列,这里需要3个参数可以看到分别是实例对象,载荷和回调函数。在这里我们先看在最开始生命4个变量分别是干什么用的,直接语义化就能猜出个大概来。

Q1:fiber通过get方法获取一些东西?

React浅谈setState

A1:可以看到,源代码实现的方法,获再结合当前调用方法的上下文可以得知,当前的fiber获取到时当前实例上的一个_reactInternalFiber的值。这个值是什么,其实是通过相应的一个set方法,将当前实例和workInProgress传入,并给赋值给当前实例的_reactInternalFiber属性。

Q2:currentTime获取当前的时间?

React浅谈setState
A2:
  • 首先判断是否正在渲染中,是的话就返回最近一次的调度时间
  • 如果不在渲染中的话,会检查是否有上次遗留的待处理的工作。
  • 如果nextFlushedExpirationTime === NoWork || nextFlushedExpirationTime === Never,来判断优先级。
  • 重新计算当前的渲染时作为调度时间,并且return;
  • 如果上次有遗留,则直接返回当前调度时间。
  • rederingTime 可以随时更新,currentSechedulerTime只有在没有新任务的时候才更新

Q3:expirationTime获取到期时间?什么鬼?

React浅谈setState
A3:
  • 在此时,会进入第一个if条件判断,通过判断当前是否存在正在执行的上下文时间,是否正在进行渲染,还是其他情况。
  • 如果存在expirationContext,则到期时间就是修改为当前的上下文执行时间。
  • 如果正在调度时间的话,判断是否处于commit阶段,是的话就设置为同步优先级,否则的话就赋值为下次渲染到期时间。
  • 如果上述情况都不满足的情况下,就会计算当前实例fiber的优先级。
  • React浅谈setState
  • 这里分为异步和同步,分别调用不同的方法进行计算,获得优先级后则和同步更新一样, 创建update并放进队列, 然后调用sheuduleWork
  • 在这里还会有交互式刷新的判断,是追踪最短待处理的交互式到期时间。 这允许我们在需要时同步刷新所有交互式更新。
  • React浅谈setState
  • 最后返回当前所需要的到期时间。
  • 此步骤和2步骤可以合并为计算优先级

Q4:update创建update队列?

A4:这个阶段就是通过createUpdate来创建一个更新对象。

React浅谈setState

在进行了一系列不可描述的过程之后,终于可以进行接下来的操作了。

React浅谈setState

首先调用flushPassiveEffects()来进行刷新,将被动影响的属性刷新一遍,接着是重头戏,调用enqueueUpdate()方法,将需要更新的fiber放入更新队列。

React浅谈setState

这里其实就是这么个原理:

第一部分

  1. 首先判断是不是只有一个fiber,只有一个fiber的话就让q1等于这个值,然后q2克隆q1
  2. 如果是有俩个fiber,则q1等于当前实例的fiber.updateQueue,q2就等于alternate.updateQueue;
  3. 如果两个fiber都没有更新队列。则q1,q2都创建新的。
  4. 只有一个fiber有更新队列。克隆以创建一个新的。
  5. 俩个fiber都有更新队列。总之就是,q1和q2都需要有一个fiber。

第二部分

  1. 当q1与q2是相等时,一位置实际上只有一个fiber,将此fiber插入到更新队列;
  2. 若q1和q2有一个是非空队列,则两个对列都需要更新;
  3. 当q1和q2两个队列都是非空,由于结构共享,两个列表中的最后一次更新是相同的。因此,只需q1添加到更新队列即可;
  4. 最后将q2的lastUpdate指针更新。

最后一步,就是掉用scheduleWork()方法,来进行最后的更新。在此方法中会根据优先级进行分片式更新。

React浅谈setState
  1. 首先调用scheduleWorkToRoot()方法,更新fiber的优先级,遍历到根组件的父级路径,并更新子组件的优先级。
  2. 为先前未计划的交互更新挂起的异步工作计数。
  3. 更新当前交互的挂起的异步工作计数。
  4. 监听更新列表的变化,返回root。

接下来,在commit阶段,一口气执行完毕。你的DOM就是最新的了。说了这么多,可能执行起来,就是短短的几十毫秒... 就比如下面

React浅谈setState

至此,setState整个过程算是完成了。

总结:这篇文章是鄙人第一次下手书写,有些地方可能表述不是很准确,可能有点啰嗦,但是我喜欢啊。俗话说万事开头难,但是过程也难啊,结果更难啊。对于代码也一样,要坚持下去,坚持下去你就得颈椎病了哦。本文有什么错误的地方,还烦请各路大神指出,鄙人是不会改滴,都会记在心里哒,上述是我对setState的理解,抛砖引玉,希望帮助大家有方向的去了解react原理机制。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Practical Django Projects, Second Edition

Practical Django Projects, Second Edition

James Bennett / Apress / 2009 / 44.99

Build a django content management system, blog, and social networking site with James Bennett as he introduces version 1.1 of the popular Django framework. You’ll work through the development of ea......一起来看看 《Practical Django Projects, Second Edition》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码