視覺化搭建 - keepAlive 模式

黃子毅發表於2023-03-27

由於 React 的特點,元件改變所在父級後會產生 Remount,而在視覺化搭建場景存在兩個特點:

  1. 自由、磁貼、流式佈局都可以透過拖拽輕鬆改變元件父元素。
  2. 大資料量下元件 Remount 的消耗不容忽視。

結合上面兩個特點,拖拽過程中或者鬆手時不可避免會產生卡頓,這就是我們這篇文章要解決的問題。

利用 createPortal 解決 Remount 問題

createPortal 可以將 React 例項渲染到任意指定 DOM 上,所以我們利用這個 API,將元件樹的元件打平,但透過 createPortal 生成到巢狀的 DOM 樹上,就同時實現了以下兩點:

  • 在 dom 結構上依然符合元件樹的巢狀描述。
  • 在 React 例項角度,沒有巢狀關係。

實現分為三步:

  1. 遍歷元件樹,根據元件樹巢狀結構生成 createPortal 的目標 dom,我們姑且稱為 keepElement,對需要掛載 keepElement 的容器位置生成 dom,稱為 keepContainer。對於沒有渲染的容器,可以先不掛載 keepElement,而是等到父容器 mount 後再將 keepElement 移過去,後面再展開說明。
  2. 遍歷元件樹,一次性打平渲染所有樹中 React 元件例項,並利用 createPortal 掛載到對應的 keepElement 上。
  3. 當資料流產生變化導致父級變化,或者佈局外掛拖動改變父級時,我們僅利用 dom api 將 keepElement 在不同的 keepContainer 之間移動,而在 React 例項視角沒有發生任何變化。

<img width=500 src="https://s1.ax1x.com/2023/03/25/ppDLFSO.png">

協議做到使用者無感知

因為實現了 dom 結構與 React 例項結構分離,因此開啟 keepAlive 模式不需要改變 componentTree 描述,也不會影響任何邏輯功能,我們只需要標記一下 keepAlive 引數即可開啟:

import { createDesigner } from 'designer'

const { Designer, Canvas, useDesigner } = createDesigner()

const App = () => {
  <Designer keepAlive={true} />
}

渲染增加了額外 dom 巢狀

keepAlive 模式唯一對功能產生的影響是增加了額外 dom 巢狀,分別是 keepContainer 與 keepElement,產生這兩層 dom 的原因分別是:

  • keepElement: 因為 React 例項 Remount 的作用範圍是該元件自身 return 的所有虛擬 dom 最終對映的真實 dom,為了保證 React 對映 dom 與 React 樹結構的對應,為了不產生 Remount 就必須要用額外的遊離態 dom 作為 createPortal 的掛載節點。
  • keepContainer: 由於不僅要知道元件產生移動時,應該將 keepElement 移動到哪個 keepContainer 下,還需要在比如容器程式碼 return children 位置突然 return null 並恢復時,重新構建 keepElement,所以我們需要監聽每一個 keepContainer 生命週期,所以需要額外生成一個 dom。

因此 keepAlive 模式勢必會打亂原有應用的 dom 結構,新增的 dom 結構在比如流式佈局時可能產生意外的定位錯誤,所以 keepAlive 模式儘量與絕對定位的佈局方式結合。

總結

keepAlive 模式可以在不改變任何協議、應用程式碼的情況下,解決跨父級移動導致的 Remount 問題,但這種設計也會引入新增 dom 結構的問題,只要儘量採用絕對定位的佈局策略,就可以避免負面影響。

討論地址是:精讀《視覺化搭建 - keepAlive 模式》· Issue #475 · dt-fe/weekly

如果你想參與討論,請 點選這裡,每週都有新的主題,週末或週一釋出。前端精讀 - 幫你篩選靠譜的內容。

關注 前端精讀微信公眾號

<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

版權宣告:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證

相關文章