ComponentLoader 與動態元件

黃子毅發表於2023-05-08

元件透過 <Canvas /> 渲染在畫布上,內容完全由元件樹 componentTree 驅動,但也有一些情況我們需要把某個元件例項渲染到元件樹之外,比如全屏、置頂等場景,甚至有些時候我們要渲染一個不在元件樹中的臨時元件,卻要擁有一系列畫布能力。

為了讓元件渲染更靈活,我們暴露出 <ComponentLoader> API:

import { createDesigner } from 'designer'

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

const App = () => {
  return (
    <Designer componentTree={/** ... */}>
      <Canvas />
      {/** 任意位置,甚至 Canvas 的元件例項內使用 ComponentLoader 載入任意元件 */}
      <ComponentLoader />
    </Designer>
  )
}

元件載入器有三種用法:按元件 ID 載入、按元件樹路徑載入、動態元件,下面分別介紹。

按元件 ID 載入

將元件樹上的某個元件渲染到任何地方,即一個元件例項渲染到 N 個地方,例項級別資訊共享,渲染為 N 份:

<ComponentLoader componentId="input1" />

如上例子,將元件 ID 為 input1 的元件渲染到目標位置。

甚至可以在元件內套元件,比如我們定義一個容器元件,內建渲染 ID 為 input1 的子元件:

const container: ComponentMeta = {
  componentName: 'container',
  // 元件 props 會自動注入 ComponentLoader
  element: ({ ComponentLoader, children }) => {
    return (
      <div>
        <ComponentLoader componentId="input1" />
        {children}
      </div>
    )
  }
}

當該元件 ID 在元件樹中被移除時,<ComponentLoader componentId="input1" /> 返回 null

按元件樹路徑載入

如果元件在元件樹上沒有 ID,或者你希望固定渲染某個位置的元件,而無論元件樹如何變化,那麼就可以採用按元件樹路徑的載入模式,將 componentId 替換為 treePath 即可:

<ComponentLoader treePath="children.0" />

如上例子,渲染的是 componentTree 根節點 children.0 位置的子元件,同樣,但元件不存在時返回 null

動態元件

如果要渲染一個不存在於元件樹的元件例項,還可以這麼用 <ComponentLoader />:

<ComponentLoader standalone componentName="card" />

即新增 standalone 表示它為一個 “孤立” 元件,即不存在於元件樹的元件,以及 componentName 指定元件名。

之所以不需要指定 componentId,是因為每個 ComponentLoader 此時都是一個唯一的例項,在 designer 內部會自動分配一個固定的元件 ID。

這麼設計非常靈活,但實現起來難度是有一些,主要注意兩點:

  1. 動態元件不存在於元件樹,但我們之前設計在元件元資訊的所有功能都要可以響應,這就要求框架程式碼不能依賴元件樹產生作用,而是將所有元件獨立儲存計算,包括元件樹上的,以及動態元件。
  2. 效能,獨立元件載入器之間的執行並無關聯,因為框架本身為響應式,為了防止頻繁重新整理或頻繁計算需要設計一套自動批處理機制,類似 React 自動 batch 的實現。

對於動態元件,我們還可以傳遞更多引數:

<ComponentLoader standalone componentName="chart" props={{ color: 'red' }}>
  <button>click</button>
</ComponentLoader>

如上例子,我們傳了額外 props 屬性,以及一個子元素給 chart 元件例項。

特別的,如果傳遞了 componentId,可以將該動態元件的 ID 固定下來,方便進行聯動:

<ComponentLoader standalone componentName="chart" componentId="abc" />

但動態元件也有一些限制,如下:

  • 該方式渲染的元件元資訊定義的 defaultPropsprops 不會生效,因為不存在於元件樹中。
  • 該元件無法透過 deleteComponent 刪除,也無法透過 setPropssetComponent 等修改,因為渲染完全由父元件控制,而不由元件樹控制。
  • 不能用 setParent 改變這種元件的位置,因為其位置在程式碼中被固定了。

總結

其實 <Canvas /> 根節點本質上等價於 <ComponentLoader treePath="" />,即從根節點開始渲染一個元件例項。

所以提供 ComponentLoader 勢必會讓業務能力更靈活,在任意位置渲染元件,甚至渲染一個不存在於元件樹的動態元件。

討論地址是:精讀《ComponentLoader 與動態元件》· Issue #482 · dt-fe/weekly

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

關注 前端精讀微信公眾號

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

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

相關文章