瀏覽器要原生實現 React 的併發更新了?

大家好,我卡頌。

要說React有什麼其他框架沒有的、獨一無二的特性,那一定是 「併發更新」。圍繞併發更新,存在兩個很有意思的現象:

這兩個現象看似矛盾,其實很好解釋 —— React18 之後的新特性,主要是面向上層框架的(主要是Next.js)。

換句話說,這些新特性(比如併發更新)主要是供框架集成,而不是開發者直接使用。

比如,併發更新的兩個核心API —— useTransitionuseDeferredValue,都是針對 「視圖切換」 的場景。

而在前端交互中,最主要的 「視圖切換」 場景就是 「路由切換」,所以包含路由功能的前端框架就會集成這兩個API

而現在,一個試驗性瀏覽器API —— View Transitions API將原生實現 「視圖切換」 功能。

他到底有什麼用?如果其他框架使用它,是不是能獲得React同樣的併發更新能力?

什麼是視圖切換?

不管是View Transitions API,還是ReactuseTransition,都是爲了提高 「視圖切換」 場景下的用戶體驗。

什麼是 「視圖切換」 ?以 view-transitions Demo[1] 舉例。這是個簡單的相冊Demo,點擊左邊圖片縮略圖,右邊會顯示大圖:

整個過程簡單來說包括 3 個步驟:

  1. 點擊縮略圖

  2. 請求大圖數據

  3. 大圖請求成功後,顯示大圖

從步驟 1 到 3 的過程就是個典型的 「視圖切換」。整個過程有很多可以優化體驗的地方,比如:

  1. 從舊圖到新圖的漸變過渡效果

  2. 點擊縮略圖發起圖片請求後,大圖區域可以先顯示舊圖(而不是立刻顯示loading效果),待新圖請求成功後再過渡到新圖

這裏解釋下第二點,對於切換類的交互,相比於 「當視圖切換時立刻顯示 loading 效果,待新視圖加載完成後過渡到新視圖」「當視圖切換時先顯示舊視圖,待新視圖加載完成後過渡到視圖」 在延遲不高的情況下體驗會更好。

除了上述這些 「體驗優化的點」,視圖切換的實現還有很多細節需要考慮,比如:

SPA(單頁應用)出現之前,網站通常是由多個頁面組成。比如下面網站的每個Tab欄,對應一個獨立網頁,其中:

Tab之間切換,瀏覽器會:

「頁面卸載」「頁面加載」 之間的白屏間隙會造成屏幕閃爍。

要優化這種場景下優化視圖切換效果,當前唯一做法是利用history API接管路由操作,將網頁變成一個SPA

既然 「視圖切換」 是如此常見的需求,且有這麼多需要考慮的因素,那瀏覽器爲什麼不原生實現呢?

於是,View Transitions API應運而生。

當前View Transitions API不支持跨頁面的視圖切換,但未來會支持

View Transitions 的使用

View Transitions API[2] 的使用很簡單,只需要用document.startViewTransition包裹視圖切換後的回調函數即可。

對於上述相冊示例,回調函數的邏輯是 「將 img 標籤 src 屬性更新爲新圖片地址」

const transition = document.startViewTransition(() ={
  galleryImg.src = /* 新圖片地址 */;
});

剩下所有跟過渡相關的實現,都由Transitions API解決。

View Transitions 實現原理

在視圖切換時,存在 2 個概念:

當使用View Transitions後,會依次做:

  1. 對頁面進行截圖,作爲舊視圖

  2. 執行傳遞給document.startViewTransition的回調

  3. DOM更新後,對更新後的頁面進行截圖,作爲新視圖

  4. 構造一棵代表過渡效果的僞元素樹,掛載在根元素(html元素)下,結構類似如下:

::view-transition
└─ ::view-transition-group(root)
   └─ ::view-transition-image-pair(root)
      ├─ ::view-transition-old(root)
      └─ ::view-transition-new(root)

其中:

對於上述相冊示例,掛載的僞元素樹結構如下:

之所以要掛載一棵僞元素樹,主要是因爲兩個原因:

  1. 開發者可以對微元素應用CSS規則

比如,上述兩個 「保存了新 / 舊視圖的截圖」 的僞元素,類似於img標籤,開發者可以對他們應用CSS動畫,當新 / 舊視圖切換時,實現自定義的過渡效果。

  1. 方便對整個頁面中不同 「視圖切換」 分組

比如,在上述相冊示例中,視圖切換的元素包括兩部分:

相冊對應的HTML結構如下:

當我們爲figcaption元素設置不同的view-transition-name

figcaption {
  view-transition-name: figure-caption;
}

會得到一棵新的僞元素樹,其中 「視圖部分」「圖片名稱部分」 僞元素是分離開的:

通過給頁面中不同HTML元素定義不同的view-transition-name屬性,就能獨立控制這個元素是視圖切換時的過渡效果。

與 React 的區別

瀏覽器原生的View Transitions APIReact中的useTransition相比,誰更強大呢?

毫無疑問,前者更強大。

這是因爲,對於View Transitions API,通過操作僞元素樹,開發者可以自定義過渡效果(就像對img元素使用CSS過渡動畫一樣簡單)。即使不使用CSS Transition,使用JS Transition也完全沒問題。

document.startViewTransition方法會返回一個transition對象實例:

const transition = document.startViewTransition(() ={
  galleryImg.src = /* 新圖片地址 */;
});

該實例包含了一系列視圖切換生命週期對應的promise,比如:

而在React中,使用useTransition後,與其說完成的是 「視圖切換」,不如說完成的是:

  1. 首先,完成狀態的切換

  2. React內部再將狀態變化映射到視圖變化

本質來說,操作視圖的是React,而不是開發者。所以,開發者很難控制過渡效果。

動效庫Framer的作者認爲,由於開發者很難控制併發更新的完整生命週期,所以很難在併發更新時表達animation效果:

簡單來說就是,開發者很難爲併發更新定製過渡效果(用CSSJS)。

總結

可以認爲,View Transitions API是比useTransition抽象程度更高、開發者可控性更高的APIuseTransition能實現的,他可以。useTransition不能實現的,他也可以。

要說缺點,View Transitions APIWeb平臺獨有的,而useTransition依賴React核心的併發更新能力,是跨端的。

當前,View Transitions API的兼容性並不好:

但是,一旦他變成可以大規模使用的API,那麼其他前端框架只要接入他,就能輕鬆獲得比React耗費大量精力實現的useTransition(以及底層的併發更新特性)更強大的視圖切換能力。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/dcPiKDGhMgX6VJ9IK413Ww