從表單抽象到表單中臺
作者:jinc
相信前端開發的同學,對錶單其實並不陌生,而且時至今日,表單應用的編寫因為React、Vue等框架的出現,也變得更加地便捷了。在前端工作中,有著很多中後臺應用-表單的開發工作量,筆者自己深陷其中,所以為了讓頭髮別掉得太快,重新去理解了表單這個東西,從而重新去思考和設計表單的開發模式,提升效率。
理解表單
其實大家都知道表單是什麼,但大多數人對它應該沒有一個明確地認識,至少我之前是沒有的。
基礎表單
<!-- https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Forms/Your_first_HTML_form -->
<form action="/my-handling-form-page" method="post">
<div>
<label for="name">Name:</label>
<input type="text" id="name" />
</div>
<div>
<label for="mail">E-mail:</label>
<input type="email" id="mail" />
</div>
<div>
<label for="msg">Message:</label>
<textarea id="msg"></textarea>
</div>
<div class="button">
<button type="submit">Send your message</button>
</div>
</form>
複製程式碼
這段程式碼完成了一個最為基礎的表單,我們來分析下,它都有什麼?
- 提交地址、提交方法
- 提示資訊
- 輸入框
- 提交按鈕 然後今時今日,這樣簡單的表單其實並不再能滿足越發複雜的應用需求了。
更豐富的表單
在有了JQ、React、Vue等等之後,網路和社群上有了更為豐富的表單元件,比如日期選擇、時間選擇器、圖片裁剪上傳等等。
//https://codepen.io/pen/?&editable=true&editors=001
const { TimePicker } = antd;
function onChange(time, timeString) {
console.log(time, timeString);
}
ReactDOM.render(
<TimePicker onChange={onChange} defaultOpenValue={moment('00:00:00', 'HH:mm:ss')} />,
mountNode
);
複製程式碼
定義表單
可不管怎麼變化,提交地址和提交方法、提示資訊、使用者輸入、提交按鈕,這些都是不可或缺的,於是我嘗試用簡練的語言來抽象一下表單是個什麼東西:
表單是一個將人機互動行為轉換為資料後提交給伺服器的視覺化前端應用。
想要理解表單,這兩個詞就尤為關鍵:
- 人機互動行為
- 轉換為資料
人機互動行為
為什麼不是表單元件(輸入框、上傳檔案、選擇框等等),而是人機互動行為?因為在表單應用的開發中,會有更多地使用者對資料進行輸入場景,而基本的表單元件只是其中的一類行為而已,如果哪天我們的表單裡面,需要使用者在一個畫板上畫圈圈呢?
轉換為資料
我們最終與伺服器進行傳遞的東西,不過是一份資料而已,而表單很重要的一個作用,就是將人機互動的行為轉換為計算機能夠儲存的資料,然後與接收方進行通訊。
所以,表單是這樣的:
高階模式-動態表單
聰明的開發者當然不想每天都重複地寫著類似的程式碼,動態表單也是因此而生的吧。
動態表單,說白了就是隻需要通過一份配置,就能生產一個表單應用。它能夠極大地提升我們的效率,元件的複用率等等。開發者從寫程式碼到了寫配置。
就算沒有對錶單進行明確的理解,動態表單的元件、框架或者庫類,其實都已經存在著很長的一段時間了。但它卻還是存在著一些問題:
- 有學習配置的成本
- 有開發和維護配置的成本
為了解決它的問題,於是筆者基於動態表單,設計了一個表單中臺。
表單中臺的設計
表單中臺是通過對錶單進行了抽象,然後單獨針對網站應用上的所有表單而設計的。
它是對一個網站應用上面的所有表單,從前端開發者對錶單相關的開發維護到使用者提交資料到服務端,這樣一個完整鏈路的抽象封裝。
表單配置的視覺化生產
表單的配置化其實就是將表單開發的邏輯,轉化成為了一種結構,在前端看來,它是個JSON或者是個物件。手動編寫表單配置是可以被視覺化的工具所替代的,這樣,表單的開發和維護就變得更加清晰、簡便了,效率也會得到提升。
一份配置對應著一個表單的時候,但我們在一個網站應用(業務)上有多種場景需要多個表單,這時候就會有多份配置,多份配置會就需要對齊進行管理,甚至需要動態化非同步載入配置。我把配置相關的事情,也一併列入表單中臺的設計之中,讓鏈路更加地完整。
說到這裡,有些人可能會聯想到一些問卷調查的網站、應用。本質上,它們是一樣的,但問卷調查應用與大家複雜地表單開發工作還是會有很大的不一樣,所以當有表單需求來了的時候,你不會告訴你的業務方說,"你去建立個問卷調查就好啦”。而它與問卷調查系統不一樣的地方就是一個商業系統與中臺系統的區別。所謂的中臺,就是用來驅動和支撐商業系統的。
而實現視覺化的手段,就是通過表單來生產配置,然後渲染表單。
視覺化生產表單配置的頁面:
表單中臺
表單中臺是一個可以完全由前端驅動的產品,因為表單裡面跟資料儲存查詢是可以相對對立的部分,不管資料跟哪個伺服器進行通訊,都是不需要關心的,標準應該有前端進行制定。這樣,它就是一個去中心化的產品,同時也具備成為一箇中臺的可能。因為它是一箇中臺,所以它也是能夠支撐和驅動各種N箇中後臺和業務發展的。
架構設計
通訊層與伺服器
通訊層磨平了與伺服器進行通訊的過程,這其中包括了配置的增刪改查,表單資料的讀寫。介面標準由前端進行了定義。
例如配置查詢的介面:
引數 | 型別 | 是否必須 | 說明 |
---|---|---|---|
formId | Long | 否 | 表單ID |
type | Long | 是 | 0表示根據userId獲取使用者的配置列表,1表示根據formId獲取某個具體配置 |
核心層
通過之前對錶單的抽象,就可以抽象出一些表單相關的JS類,這個些類本質上其實都是一些資料相關的操作,包括但不限於:
- 表單資料驗證
- 表單資料讀寫
- 表單元素的基本屬性
/**
* 定義一些標準的表單屬性
* - defaultValue
* - key
* - dataSource
* - disabled
* - size
* - title/labelText
*
* 方法:
* - onChange
* - setValue
* - verify
* @param {Object} config
*/
initFormProps(config){
let onChange = (value) => {
this.setValue(value)
this.formChange(this.key,value)
}
let getValue = () => {
return this._value
}
return {
onChange,
getValue,
size: this.config.size,
dataSource: this.dataSource || [],
disabled: this.disabled,
defaultValue: this.defaultValue,
value: this._value,
key: this.key,
block: this.config.block === "true" || this.config.block === "true",
title: this.title || this.labelText,
labelText: this.labelText || this.title,
_type: this.type
}
}
複製程式碼
有了這些,就可以在渲染層,進行多框架的渲染對接了。
業務層
在輸出的時候,同時輸出了渲染元件和配置生產元件,配置的生產元件可以不進行上線,只要對接業務介面即可;渲染元件自動拉取對應場景的表單配置進行渲染。
所以,只要一次的接入,後續的表單開發工作,就是三步:
1.(未有滿足的表單元件時)開發自定義的表單元件 2. 在配置生產元件建立表單 3. 在對應場景接入渲染元件
總結
開發者的開發歷程通常會有四個階段:寫程式碼、寫配置、視覺化生產、中臺。特別是在中後臺的應用場景中,這樣路徑似乎都是有效的。
本文只是說到了表單的中臺,其實,這個思路是可以被複制的,
從表單頁面的視覺化,到表格頁面的視覺化,再到中後臺站點的視覺化,路徑與架構設計都會大致相同。因此,為了解放生產力,還有很長的路要走。
專欄其他文章
FE One