React 目前主要的问题有两个html
其中第一个问题会在设备计算能力差(好比 mobile device)的状况下被放大,咱们能够把这个问题和 CPU 绑在一块儿(CPU-bound)react
第二个问题会在设备网络状况差(好比 3G slow network)的状况下被放大,咱们能够把这个问题和 IO 绑在一块儿(IO-bound)git
今天咱们主要来说下,React concurrent mode 是如何解决第一个问题的,第二个问题会在后面的代码实战文章再仔细讲下。github
首先,这个问题并非一个 performance 问题,而是一个调度 (scheduling) 问题,因此提升处理速度并不能彻底解决这个问题,就像提升网速并不能解决 HTTP/1.1 协议的线头阻塞 (Head-of-line blocking) 问 题。浏览器
这个问题的本质是,浏览器的 main thread 是单线程的,短期大量 CPU consuming 的 task 被加到了 call stack,致使 event loop 在好几个周期都没有空闲去处理 queue 里面的用户交互 (click, scroll, etc.),从而用户感知到了卡顿。网络
这两个问题在目前能不能解决呢?其实是能够的,可是解决的方式不够优雅,或者说,没有彻底解决。好比第一个问题在 Table 搜索的时候很常见,若是 Table 数据特别多,搜索的时候的卡顿是必定的。解决方法也很简单,使用 debounce 函数在用户连续输入的时候,不进行操做,等用户输入暂停再搜索。这种 workaround 至关于解决了一半的问题,用户能接受最好,不能接受,其实也没有别的更好的方法了。异步
解决这个问题的原理其实很简单,HTTP/2 采用分帧 (Frame) 的方式解决的 HTTP/1.1 的线头阻塞问题,咱们也能够将大量的更新任务 (Batch update task) 拆分红一个个小的 task,从而让 event loop 有机会处理用户交互的 callback。async
React Fiber 就是 React Team 一直在持续开发用来解决上面问题的新 algorithm 或者叫 reconciler。ide
Fiber 的原理挺复杂的,简单说就是原来须要一口气放到 call stack 里面执行的一堆任务,被放到了 heap 里面由一个 scheduler 来调度执行。函数
其实 React Team 应该在一开始就意识到这个问题了,因此 setState()
被设计成了异步的,但就像官方文档里说的,他们并无彻底利用 asynchronous update (not taking advantage of this) 。仅仅异步是不够的,blocking 仍是会 blocking,只有 scheduling the update 才能实现可中断渲染 (interruptible rendering)
Concurrent Mode 指的就是 React 利用上面 Fiber 带来的新特性的开启的新模式 (mode)。目前 React 实验版本容许用户选择三种 mode
Concurrent Mode 其实开启了一堆新特性,其中有两个最重要的特性能够用来解决咱们开头提到的两个问题
其中 Suspense 能够用来解决请求阻塞的问题,UI 卡顿的问题其实开启 concurrent mode 就已经解决的,但如何利用 concurrent mode 来实现更友好的交互仍是须要对代码作一番改动的,后面的文章会用实际代码的方式讲下这两个的使用,这里给一个大数据下的不一样 mode 下的页面更新速度的对照 demo
上面 Fiber 的 update 机制,有个细节问题就是,既然如今的更新是能够被打断的,那若是打断的操做影响了更新怎么办?好比说有个 background-color 我原来要变成绿色,可是用户点击了一个按钮,background-color 要变成红色,那最终结果会怎样?指望确定是红色,否则就是 bug 了。所以 React 这里作了处理,更新到一半的 work 是能够被丢弃而从新开始的。
这样的作法没错,可是就带来一个问题,有些生命周期函数可能会被屡次调用。
咱们能够将全部的生命周期函数分红两类,分别运行在 Render 阶段和 Commit 阶段。
Render phase 简单说就是决定究竟渲染什么的阶段,涉及绝大多数生命周期。
Commit phase 简单说就是将须要渲染的内容推到 DOM (若是是 DOM 环境) 的阶段。
componentDidMount()
, componentDidUpdate()
, componentWillUnmount()
这三个生命周期函数 (lifecycle method) 运行在 commit 阶段,其他的生命周期函数均可以简单理解为运行在 render 阶段。
咱们上面说更新到一半的 work 有可能被从新启动,这就意味着,像是 render
,shouldComponentUpdate
等等这些在 render 阶段的生命周期函数并非像你指望的那样只执行一个,而是有可能被执行屡次。
解决方式有两种:
React Concurrent Mode 经过启用 Fiber reconciler 实现了可中断渲染 (interruptible render),从而让一系列的交互优化变成了可能。下篇文章会使用实际代码来说解究竟如何正确使用 Concurrent Mode API