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