今年十月,React Conf 2019 傳出了令人興奮的消息——Concurrent Mode、 Suspense for Data Fetching 終於面世。React Fiber 計畫鋪的路,要開始綻放光芒了。
在前後端分離的現在,前端工程師們需要頻繁地呼叫 API 拿取資料,衍生出各種呼叫 API 的模式,以及大量的 Loading 狀態和 Race Condition 的風險。
沒經驗的工程師會被各種詭異的顯示結果耍得團團轉,而就算是有經驗的工程師,每個人都有各自習慣的拿取資料模式,並沒有統 一規範,造成一個問題有多種解法。不同解法帶來的使用者體驗不盡相同(例如資料要 一次全拿,還是分批拿)最終導致產品的功能體驗缺乏一致性。
在眾多狀態中,Loading 狀態最容易被忽略, 卻對使用者體驗至關重要。從資料回來前, 該怎麼保持畫面的完整性、降低使用者的感知時間,到資料回來後,要如何不造成畫面的跳動或閃爍,其中有許多細節需要注意。 甚至好不容易抓回資料,卻在使用者進行操作時,因資料筆數過多,導致運算速度跟不上而感到卡頓。這時該用防抖抑或節流,又是一個典型的前端回答,It depends.。
追求良好使用者體驗的道路上充滿荊棘,於 是 React 射出了三發銀色子彈:
畫面卡頓的起因,通常是因為 State 改變造成的渲染,阻止了畫面立即更新。例如下圖,當使用者輸入文字時,下方的圖表會跟著變化,大量的 DOM 變更,於是阻斷了輸入框即時更新文字。以往我們會有 setTimeout、 防抖、節流等方式優化使用者體驗,但仍能感受到卡頓,而 Concurrent Mode 能透過中斷、暫停渲染解決這個問題。
背後的原理是呼叫原生的 Web API—— requestIdleCallback,但這 API 本身支援度不高,因此 React 自己去做 Polyfill,來實現 Concurrent 的效果。
要讓資料和畫面同步渲染,需要 Suspense 和這次的新 Hook——useTransition 配合才能做到。
分成兩個步驟:首先是拿資料,接著則是處理等待狀態(Loading)。
第一個步驟需要 Suspense for Data Fetching—— 之前 Suspense 只能載入程式碼,現在可以載入資料了。
Data Fetching 後,接著要處理等待狀態,此時就需要 useTransition。這是因為在拿資料的過程中,不可避免地需要等待,因此通常需要處理以下問題:
透過 Suspense 和 useTransition 的配合,可以 讓 Component 在所有資料到達之前就開始在記憶體中渲染,減少 Loading 狀態,避免畫面不停跳動,提升使用者體驗。
以往要避免畫面卡頓,並讓資料、畫面同步渲染,需要各式各樣的特殊解法,而這些解法往往都只能針對特定情境做優化,或是添加冗長的程式碼,無法廣泛使用到各個專案中。
之前若要做到畫面按照編排順序出現,我們可能需要使用大量的 promise,當編號 1 的區塊資料回來後,再開始拿編號 2 區塊的資料,這不僅造成使用者等待時間增加,程式碼也冗長。 但 React 這次的更新,讓開發者可以用簡潔的語法,就能達到效果,讓使用者體驗的優化可以跨專案且長期存在,也不會造成維護上的困難。
“A great developer experience only matters if it’s in service of delivering a great user experience.”- Tom Occhino 現代前端開發日趨成熟且複雜,想用心優化使用者體驗,往往意味著注意力要分散到構建配置和優化,而不是業務需求,在排優先順序時多被忽略或推遲;但這次 React 的更新,把使用者體驗研究的成果融入框架中,同時提升開發者和使用者體驗。雖然目前的生態系要跟上 Concurrent Mode 還有漫漫長路要走,但至少我們往正確的方向邁出了一步。