精讀《如何抽象視覺化搭建》

黃子毅發表於2023-01-09

在做任何視覺化搭建專案時,第一步都要思考如何抽象。

如果不抽象,當搭建專案做到後期可能會出現 API 雜亂,難以維護的問題;做到一半甚至會懷疑為什麼需要一個搭建框架,懷疑把框架去掉會不會效率更高;在後期發現不能自然的水平擴充到儀表盤、大屏、表單搭建場景等。

所以如果在維護一套視覺化搭建系統時,不管這個系統的上層是 BI、大屏、表單填報,還是腦圖也好,無論是什麼,都要先思考一下這些系統背後的底層是什麼,需不需要抽象,抽象的意義和價值在哪。

以下結合筆者的經驗,嘗試給出一種思考角度。

精讀

什麼是視覺化搭建

表單搭建、中後臺應用搭建、BI 儀表盤搭建、大屏搭建都算視覺化搭建,因為它們都是在一個畫布上拖拖拽拽完成的。

那麼元件配置表單算搭建嗎?聚焦單元件分析的視覺化探索呢?幻燈片呢?

比如元件配置表單,它基於 UI 元件樹抽象的話,就是視覺化搭建,但如果基於表單結構抽象,就是 JsonSchema,但真的所有業務場景都是資料完全對映 UI 嗎?不一定,因為 UI 可以為了使用者操作方便而加入更多輔助元素,甚至把一個屬性拆成多個 UI 填寫,所以基於視覺化搭建,也就是 UI 元件樹抽象的一定可以覆蓋所有表單場景,但不一定是描述效率最高的方式。

如果每種視覺化搭建場景都定義一套協議與實現,那按照搭建平臺的複雜度,想同時維護兩個類搭建平臺的成本一定是兩倍,而且不同維護人員很難交流。又或者某些可以按照搭建思路解決的場景,因為實現時經驗不足,沒有進行抽象,甚至進行了另一套定製抽象,回過頭來看可能積重難返,團隊不得不接受多套笨重實現的現狀。

所以建議將這些場景都視為視覺化搭建場景,用一套介面描述結構、API 方法,讓看似百花齊放的編輯器之下擁有統一的上下文與實現。

視覺化搭建的分層

對於不同種類的視覺化搭建平臺,我們嘗試尋找其分層設計的最大公約數。如果把視覺化搭建底層設定為邏輯層,即這個層是 UI 無關的,僅關心元件樹結構、邏輯功能,那麼對於每種平臺的分層應該是這樣的:

  1. 表單搭建:邏輯層、表單聯動協議層、表單控制元件、業務層。
  2. 中後臺應用搭建:邏輯層、應用聯動協議層、應用控制元件、業務層。
  3. BI 儀表盤:邏輯層、篩選聯動協議層、視覺化控制元件、業務層。
  4. 大屏搭建:邏輯層、畫布編輯控制器層、視覺化控制元件和基礎圖形控制元件、業務層。

最底層的邏輯層應該可以統一所有型別搭建系統,併成為開發人員統一上下文的。它可以包含以下基礎能力:

  1. 定義元件樹結構。
  2. 定義元件元資訊。
  3. 按照元件樹結構遞迴渲染畫布。
  4. 支援佈局、取數、聯動、篩選、校驗等一系列擴充能力,業務可根據需要定製。
  5. 提供所有業務層都需要的能力,比如效能最佳化的元件凍結、狀態管理、對元件樹增刪改查的 API。

在邏輯層完備後,再開發上層應用就會輕鬆很多,只要註冊元件、根據業務需要在元件樹初始化或元件初始化,或元件元資訊註冊時新增定製邏輯,與系統功能對接,並補充業務特色的如自定義佈局能力,這樣就可以用簡單的三言兩語說清楚整個系統是如何設計的。

邏輯層存在的必要性

再回到問題的根源:對邏輯層做統一的抽象到底是不是多餘的?

要回答這個問題,需要先了解我們手頭裡有哪些工具:基礎開發工具 html、js、css,並且 html 也提供了一套標準化的 xml 結構;vue、react 等開發框架,基礎元件、應用生命週期與事件定義。理論上基於這些,我們就可以直接上手寫一個視覺化搭建平臺了,似乎也可以不抽象。但真正要上手時,一定會遇到以下幾個通用問題需要處理:

定義元件樹結構

無論做表單搭建、報表搭建、大屏搭建還是腦圖畫布,第一個想到的肯定是如何描述這個畫布結構,而無論畫布是橫著排還是豎著排,橫豎都是一棵樹。HTML 樹不能直接搬過來,一是 HTML 樹的完整結構太大而我們需要的更精簡的結構,二是業務層框架一般都先有一套虛擬樹再轉化為 dom 樹,因果關係也沒法反過來。而這棵樹也完全可以做最大程度的抽象,即定義元件 ID、元件名、屬性(Props)、子節點。

定義對元件樹增刪改查函式

有了元件樹肯定需要對其進行增刪改查操作,因為無法基於 document API,上層框架如 vue、react 也不提供對任何標準元件樹的增刪改查 API,這部分能力勢必要手動實現。

生命週期

假設完全依賴 React 框架提供的元件生命週期,是可以完成大部分業務邏輯,但這意味著定義不夠精細化。比方說,我們在元件 Mount 的實際監聽了聯動、實現取數、設定凍結等等效果,雖然也可以實現,但會遇到要不要抽象的問題:

  • 如果不抽象,業務程式碼就會亂糟糟的,比較難讀。
  • 如果抽象,就要把聯動、取數、凍結等等模組歸類,封裝成函式,甚至可以提供主動呼叫機制,UI 與邏輯解耦,但當業務層精細的去做這件事就會發現,這就是在做框架層的抽象工作,所以還不如一開始就把這些生命週期抽象到框架裡。

邏輯層有兩個核心結構,第一個是元件樹結構,包含了對每個元件例項的定義;第二個是元件元資訊結構,包含了對每個元件的元資訊描述,大概如下圖所示:

<img width=400 src="https://user-images.githubusercontent.com/7970947/211183628-75469a31-54cf-446a-9df8-a18bb41508db.png">

邏輯層的難點就是在元資訊定義足夠多、足夠通用的生命週期回撥函式,並且這些回撥函式還能儘可能的功能正交。

元件渲染

通常一棵樹按照 json 結構描述自頂向下自動渲染就可以了,但也有一些時候,比如內嵌一個富文字元件,而富文字內又嵌入一些畫布元件,這些元件需要像普通畫布元件一樣可互動,此時就有 渲染一個不存在於元件樹的元件例項 的需求,而這樣的動態元件又要無感知的滿足上面所說的各類生命週期,這也是不小的工作量。

功能的擴充抽象

等視覺化搭建平臺正式維護時,就至少會遇到元件版本升級、不同型別的佈局方案對接、三方元件註冊等需求,這些功能如何加入到現有的搭建平臺,而不讓其他功能感知,是需要精心設計的。如果邏輯層把這一點抽象好,在每個功能設計一個鉤子,實現一個功能時無需感知其他功能,那平臺的功能擴充就會保持一個恆定的速度,不隨功能增加而變得難以維護。

可見,視覺化搭建不斷迭代的過程就是自身不斷抽象的過程,邏輯層實現的好壞直接影響到後期的維護性與擴充性,所以好好設計邏輯層可以讓開發事半功倍。

元件配置表單要不要用搭建方案做

元件配置直接用表單方案而不是搭建,似乎是最容易想到的。但當每個元件都要自定義配置,我們就不得不選擇基於 JsonSchema 描述的表單方案,但這與搭建應用本身的技術棧割裂了,隨著聯動功能的要求越來越多,會越來越發現小小的表單渲染引擎維護得越來越複雜,甚至複雜度與畫布不分上下,此時再嘆息兩邊技術棧不統一就已經晚了。

換個角度想一下,搭建應用不也要考慮元件間聯動嗎?從表單值能力來看,搭建場景並不要求每個元件都擁有一個值,反倒是可以將元件任意 props 屬性看作表單值更具有 “彈性”,我們可以擴充任意 Key 作為表單值。

另外,從資料結構觸發來描述表單看似很美好,但當表單變得越來越複雜,UI 越來越定製後,勢必引入新的 UI 節點或者新的結構描述,與其後期擴充到一個不純淨的 JsonSchema 結構,不如一開始就放棄這個幻想,用 UI 元件樹結構描述表單,這樣事情就變得簡單了:“先描述元件樹,再定義每個節點分別用什麼元件渲染,響應表單的哪部分 Key”。

總結

總結一下,回到主題,抽象視覺化搭建的方法是分層:以邏輯層打底,提供一套標準規範與 API 介面,上層註冊元件、實現佈局,一切圍繞著標準化的邏輯層進行擴充。

而視覺化搭建的每一層都可以分別寫單元測試,保證最終變化的程式碼只有業務層的對接部分,應用的穩定性就提高了。

最後提一個思考題:你是覺得視覺化搭建應該如何抽象?如果想要做到每一層獨立正交,你會如何設計 API 呢?

討論地址是:精讀《如何抽象視覺化搭建》· Issue #463 · dt-fe/weekly

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

關注 前端精讀微信公眾號

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

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

相關文章