摘要:調(diào)度系統(tǒng),支持不同渲染優(yōu)先級(jí),對(duì)進(jìn)行調(diào)度。調(diào)度帶來的限制調(diào)度系統(tǒng)也存在兩個(gè)問題。調(diào)度系統(tǒng)能力有限,只能在瀏覽器提供的能力范圍內(nèi)進(jìn)行調(diào)度,而無法影響比如的渲染回收周期。精讀關(guān)于調(diào)度系統(tǒng)的剖析,可以讀深入剖析這篇文章,感謝我們團(tuán)隊(duì)的淡蒼提供。
1. 引言
這次介紹的文章是 scheduling-in-react,簡(jiǎn)單來說就是 React 的調(diào)度系統(tǒng),為了得到更順滑的用戶體驗(yàn)。
畢竟前端做到最后,都是體驗(yàn)優(yōu)化,前端帶給用戶的價(jià)值核心就在于此。
2. 概述文章從 Dan 在 JSConf 提到的 Demo 說起:
這是一個(gè)測(cè)試性能的 Demo,隨著輸入框字符的增加,下方圖表展示的數(shù)據(jù)量會(huì)急速提升。在 Synchronous 與 Debounced 模式下的效果都不盡如人意,只有 Concurrent 模式下看起來是順暢的。
那么為什么普通的 Demo 會(huì)很卡呢?
這就涉及到瀏覽器 Event Loop 規(guī)則了。
JS 是單線程的,瀏覽器同一時(shí)間只能做一件事情,而肉眼能識(shí)別的刷新頻率在 60FPS 左右,這意味著我們需要在 16ms 之內(nèi)完成 Demo 中的三件事:響應(yīng)用戶輸入,做動(dòng)畫,Dom 渲染。
然而目前幾乎所有框架都使用同步渲染模式,這意味著如果一個(gè)渲染函數(shù)執(zhí)行時(shí)間超過了 16ms,則不可避免的發(fā)生卡頓。
總結(jié)一下有兩個(gè)主要問題:
長時(shí)間運(yùn)行的任務(wù)造成頁面卡頓,我們需要保證所有任務(wù)能在幾毫秒內(nèi)完成,這樣才能保證頁面的流暢。
不同任務(wù)優(yōu)先級(jí)不同,比如響應(yīng)用戶輸入的任務(wù)優(yōu)先級(jí)就高于動(dòng)畫。這個(gè)很好理解。
React 調(diào)度機(jī)制為了解決這個(gè)問題,React16 通過 Concurrent(并行渲染) 與 Scheduler(調(diào)度)兩個(gè)角度解決問題:
Concurrent: 將同步的渲染變成可拆解為多步的異步渲染,這樣可以將超過 16ms 的渲染代碼分幾次執(zhí)行。
Scheduler: 調(diào)度系統(tǒng),支持不同渲染優(yōu)先級(jí),對(duì) Concurrent 進(jìn)行調(diào)度。當(dāng)然,調(diào)度系統(tǒng)對(duì)低優(yōu)先級(jí)任務(wù)會(huì)不斷提高優(yōu)先級(jí),所以不會(huì)出現(xiàn)低優(yōu)先級(jí)任務(wù)總得不到執(zhí)行的情況。
為了保證不產(chǎn)生阻塞的感覺,調(diào)度系統(tǒng)會(huì)將所有待執(zhí)行的回調(diào)函數(shù)存在一份清單中,在每次瀏覽器渲染時(shí)間分片間盡可能的執(zhí)行,并將沒有執(zhí)行完的內(nèi)容 Hold 住留到下個(gè)分片處理。
Concurrent 的正式 API 會(huì)在 2019 Q2 發(fā)布,現(xiàn)在可以通過
ReactDOM.render(, rootElement );
只申明這個(gè)是不夠的,因?yàn)槲覀冞€沒有申明各函數(shù)執(zhí)行的優(yōu)先級(jí)。我們可以通過 npm i scheduler 包來申明函數(shù)的優(yōu)先級(jí):
import { unstable_next } from "scheduler"; function SearchBox(props) { const [inputValue, setInputValue] = React.useState(); function handleChange(event) { const value = event.target.value; setInputValue(value); unstable_next(function() { props.onChange(value); sendAnalyticsNotification(value); }); } return ; }
在 unstable_next() 作用域下的代碼優(yōu)先級(jí)是 Normal,那么產(chǎn)生的效果是:
如果 props.onChange(value) 可以在 16ms 內(nèi)執(zhí)行完,則與不使用 unstable_next 沒有區(qū)別。
如果 props.onChange(value) 的執(zhí)行時(shí)間過長,可能這個(gè)函數(shù)會(huì)在下次幾次的 Render 中陸續(xù)執(zhí)行,不會(huì)阻塞后續(xù)的高優(yōu)先級(jí)任務(wù)。
調(diào)度帶來的限制調(diào)度系統(tǒng)也存在兩個(gè)問題。
調(diào)度系統(tǒng)只能有一個(gè),如果同時(shí)存在兩個(gè)調(diào)度系統(tǒng),就無法保證調(diào)度正確性。
調(diào)度系統(tǒng)能力有限,只能在瀏覽器提供的能力范圍內(nèi)進(jìn)行調(diào)度,而無法影響比如 Html 的渲染、回收周期。
為了解決這個(gè)問題,Chrome 正在與 React、Polymer、Ember、Google Maps、Web Standars Community 共同創(chuàng)建一個(gè) 瀏覽器調(diào)度規(guī)范,提供瀏覽器級(jí)別 API,可以讓調(diào)度控制更底層的渲染時(shí)機(jī),也保證調(diào)度器的唯一性。
3. 精讀關(guān)于 React 調(diào)度系統(tǒng)的剖析,可以讀 深入剖析 React Concurrent 這篇文章,感謝我們團(tuán)隊(duì)的 淡蒼 提供。
簡(jiǎn)單來說,一次 Render 一般涉及到許多子節(jié)點(diǎn),而 Fiber 架構(gòu)在 Render 階段可以暫停,一個(gè)一個(gè)節(jié)點(diǎn)的執(zhí)行,從而實(shí)現(xiàn)了調(diào)度的能力。
React 調(diào)度能力的限制這意味著,如果你的 React 應(yīng)用目前是流暢的,開啟 Concurrent 并不會(huì)對(duì)你的應(yīng)用帶來性能體驗(yàn)上的提升,如果你的 React 應(yīng)用目前是卡頓的,或者在某些場(chǎng)景下是卡頓的,那么 Concurrent 或許可以挽救你一下,帶來一些改變。
正如《深入剖析 React Concurrent》一文提到的,如果你的應(yīng)用沒有性能問題,就不要指望 React 調(diào)度能力有所幫助了。
這也是在說,如果一段代碼邏輯不存在性能問題,就不需要使用 Concurrent 優(yōu)化,因?yàn)檫@種優(yōu)化是無效的。我們需要能分辨哪些邏輯需要優(yōu)化,哪些邏輯不要。
從現(xiàn)在開始嘗試 Function Component為了配合 React Schedule 的實(shí)現(xiàn),學(xué)會(huì)使用 Function Component 模式編寫組件是很重要的,因?yàn)椋?/p>
Class Component 的生命周期概念阻礙了 React 調(diào)度系統(tǒng)對(duì)任務(wù)的拆分。
調(diào)度系統(tǒng)可能對(duì) componentWillMount 重復(fù)調(diào)用,使得 Class Component 模式下很容易寫出錯(cuò)誤的代碼。
Function Component 遵循了更嚴(yán)格的副作用分離,這使得 Concurrent 執(zhí)行過程不會(huì)引發(fā)意外效果。
React.lazy與 Concurrent 一起發(fā)布的,還有 React 組件動(dòng)態(tài) import 與載入方案。正常的組件載入是這樣的:
import OtherComponent from "./OtherComponent"; function MyComponent() { return (); }
但如果使用了 import() 動(dòng)態(tài)載入,可以使用 React.lazy 讓動(dòng)態(tài)引入的組件像普通組件一樣被使用:
const OtherComponent = React.lazy(() => import("./OtherComponent")); function MyComponent() { return (); }
如果要加入 Loading,就可以配合 Suspense 一起使用:
import React, { lazy, Suspense } from "react"; const OtherComponent = lazy(() => import("./OtherComponent")); function MyComponent() { return (Loading...
和 Concurrent 類似,React.lazy 方案也是一種對(duì)性能有益的組件加載方案。
調(diào)度分類調(diào)度分 4 個(gè)等級(jí):
Immediate:立即執(zhí)行,最高優(yōu)先級(jí)。
render-blocking:會(huì)阻塞渲染的優(yōu)先級(jí),優(yōu)先級(jí)類似 requestAnimationFrame。如果這種優(yōu)先級(jí)任務(wù)不能被執(zhí)行,就可能導(dǎo)致 UI 渲染被 block。
default:默認(rèn)優(yōu)先級(jí),普通的優(yōu)先級(jí)。優(yōu)先級(jí)可以理解為 setTimeout(0) 的優(yōu)先級(jí)。
idle:比如通知等任務(wù),用戶看不到或者不在意的。
目前建議的 API 類似如下:
function mytask() { ... } myQueue = TaskQueue.default("render-blocking")
先創(chuàng)建一個(gè)執(zhí)行隊(duì)列,并設(shè)置隊(duì)列的優(yōu)先級(jí)。
taskId = myQueue.postTask(myTask,);
再提交隊(duì)列,拿到當(dāng)前隊(duì)列的執(zhí)行 id,通過這個(gè) id 可以判斷隊(duì)列何時(shí)執(zhí)行完畢。
myQueue.cancelTask(taskId);
必要的時(shí)候可以取消某個(gè)函數(shù)的執(zhí)行。
4. 總結(jié)隨著 Hooks 的發(fā)布,即將到來的 Concurrent 與 Suspense 你是否準(zhǔn)備好了呢?
筆者希望大家一起思考,這三種 API 會(huì)給前端開發(fā)帶來什么樣的改變?歡迎留言!
討論地址是:精讀《Scheduling in React》 · Issue #146 · dt-fe/weekly
如果你想?yún)⑴c討論,請(qǐng) 點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀 - 幫你篩選靠譜的內(nèi)容。
關(guān)注 前端精讀微信公眾號(hào)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/103529.html
摘要:引言本周精讀的文章是,看看作者是如何解釋這個(gè)多態(tài)性含義的。讀完文章才發(fā)現(xiàn),文章標(biāo)題改為的多態(tài)性更妥當(dāng),因?yàn)檎恼露荚谡f,而使用場(chǎng)景不局限于。更多討論討論地址是精讀的多態(tài)性如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。 1 引言 本周精讀的文章是:surprising-polymorphism-in-react-applications,看看作者是如何解釋這個(gè)多態(tài)性含...
摘要:引言這個(gè)是針對(duì)的。一般結(jié)合使用,因?yàn)檎?qǐng)求級(jí)別的緩存與具有頁面攔截功能的最配。本周精讀的文章是,介紹了瀏覽器緩存接口的基本語法。包含任意命名空間,可以通過創(chuàng)建或訪問。精讀筆者利用實(shí)現(xiàn)了純?yōu)g覽器端的后端渲染。前端精讀幫你篩選靠譜的內(nèi)容。 1 引言 caches 這個(gè) API 是針對(duì) Request Response 的。caches 一般結(jié)合 Service Worker 使用,因?yàn)檎?qǐng)求級(jí)...
摘要:拿到的都是而不是原始值,且這個(gè)值會(huì)動(dòng)態(tài)變化。精讀對(duì)于的與,筆者做一些對(duì)比。因此采取了作為優(yōu)化方案只有當(dāng)?shù)诙€(gè)依賴參數(shù)變化時(shí)才返回新引用。不需要使用等進(jìn)行性能優(yōu)化,所有性能優(yōu)化都是自動(dòng)的。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 Vue 3.0 的發(fā)布引起了軒然大波,讓我們解讀下它的 function api RFC 詳細(xì)了解一下 Vue 團(tuán)隊(duì)是怎么想的吧! 首先官方回答了幾個(gè)最受關(guān)注的...
摘要:會(huì)自動(dòng)觸發(fā)函數(shù)內(nèi)回調(diào)函數(shù)的執(zhí)行。因此利用并將依賴置為使代碼在所有渲染周期內(nèi),只在初始化執(zhí)行一次。同時(shí)代碼里還對(duì)等公共方法進(jìn)行了包裝,讓這些回調(diào)函數(shù)中自帶效果。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 react-easy-state 是個(gè)比較有趣的庫,利用 Proxy 創(chuàng)建了一個(gè)非常易用的全局?jǐn)?shù)據(jù)流管理方式。 import React from react; import { stor...
摘要:未來可能成為官方之一。討論地址是精讀組件如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 為什么要了解 Function 寫法的組件呢?因?yàn)樗谧兊迷絹碓街匾?那么 React 中 Function Component 與 Class Component 有何不同? how-are-function-components-di...
閱讀 3245·2023-04-26 01:31
閱讀 1892·2023-04-25 22:08
閱讀 3430·2021-09-01 11:42
閱讀 2823·2019-08-30 12:58
閱讀 2165·2019-08-29 18:31
閱讀 2429·2019-08-29 17:18
閱讀 3064·2019-08-29 13:01
閱讀 2551·2019-08-28 18:22