獵戶座——基於組件的前端配置化引擎設計
摘要:配置化引擎根據組件配置文件,對頁面搭建者配置的內容進行處理,最終向組件傳入參數內容如下:。* 參數類型(type),表示將要展示在獵戶座中展示的配置該參數的方式,以及將要傳入組件的props的數據類型。
“
爲了應對紛雜繁複的業務需求,配置化效率提升已經成爲每一位研發工作中越來越重要的一部分。
”
作爲前端研發,需要不斷總結經驗、抽象業務模式和規律,應用組件化思想結合配置化設計思路,以大幅提升前端頁面的配置化開發效率。
在進行前端頁面開發過程中,依照前端組件化思想,無論使用React框架還是Vue框架,頁面的組織結構都是以組件爲基本單位進行的。前端研發可以基於現有組件,通過設置組件參數完成頁面搭建。
基於這一規律,我們可以設計一種配置化引擎。一方面由組件的提供者聲明組件能夠接受的屬性;一方面由頁面的搭建者配置組件的屬性的值。而渲染引擎則通過解析雙方提供的配置信息,動態化的渲染前端頁面。
下面我們將分別介紹對配置化引擎的設計,並且從屬性定義、數據請求、層級關係、跨組件交互這幾個方面的介紹配置化引擎對組件屬性的處理。
配置化引擎
組件提供者除組件代碼外,額外提供一個組件配置文件。在這個配置文件中,組件提供者需要聲明:
* 該組件可以接受的屬性:包括屬性的參數名、類型、默認值等。
* 該組件中包含的子組件:如組件可以接受一個或多個子組件、組件可以接受固定數量或不定數量的子組件等。
* 該組件可以觸發或處理的交互事件:如組件中包括按鈕,則可以觸發按鈕的點擊事件;如組件中包含圖表,則可以處理圖表重繪事件。
頁面搭建者根據頁面需求,通過頁面配置文件,將組件組裝成爲目標頁面。在頁面配置文件中,頁面搭建者可以:
* 設置組件屬性的值或值的來源。
* 通過嵌套數據結構表示組件與子組件之間的層級關係。
* 通過創建引用關係,將觸發事件的組件和處理事件的組件關聯在一起。
而配置化引擎部分:
1、頁面渲染引擎根據頁面配置文件進行:
* a. 渲染頁面框架:如頁面標題、是否進行移動端適配、是否啓用埋點等。
* b. 預處理數據請求:對被標記爲需要首先請求的接口預先請求數據。如用戶信息接口等。
* c. 構建虛擬DOM樹:根據組件層級結構構建虛擬DOM樹,用於在各組件DOM元素內渲染組件。
* d. 深度優先遍歷 渲染組件:根據組件層級結構,以深度優先的順序進行遍歷,依次渲染組件。
2、組件渲染引擎根據頁面配置文件中當前組件的數據及組件配置文件進行:
* a. 處理組件展示邏輯:根據頁面搭建者設置的邏輯條件,判斷當前頁面參數、接口數據等條件下,該組件是否需要展示。
* b. 處理靜態屬性:根據組件配置文件中的聲明,從頁面配置數據中獲取、處理、格式化組件所需要的屬性。
* c. 處理數據請求:從頁面配置數據中獲取數據請求的接口信息,請求接口或從緩存中返回數據;根據組件配置文件中的聲明,對返回數據進行處理,並將格式化後的數據傳入組件。
* d. 處理組件交互方法:根據頁面配置數據,構造一個事件處理函數,並傳入組件。
* e. 處理子組件容器:將頁面渲染引擎構造的虛擬DOM樹中,作爲當前組件子組件的DOM結構傳入組件。
* f. 渲染組件:在虛擬DOM元素中渲染組件內容,將之前處理的各類數據傳入組件。
* g. 綁定組件交互方法:將組件可以處理的交互方法綁定至組件配置引擎。
應用上述前端配置化引擎,我們開發了可視化前端頁面搭建工具獵戶座(orion.jd.com)。
通過在配置化引擎上層封裝可視化用戶界面,讓頁面搭建者直接在獵戶座系統中通過可視化界面,實現0開發基礎快速搭建頁面。
爲此我們設計了配置化中間件,將對頁面配置文件的編輯修改和解析讀取封裝爲一系列命令集,將可視化用戶界面和頁面/組件配置引擎進行解耦。爲將來把可視化用戶界面從當前的瀏覽器端向PC客戶端、移動端擴展;把配置化引擎從React框架向原生、Flutter等技術棧擴展等打下基礎。
接下來,我將對配置化引擎部分進行詳細的介紹。
屬性定義
在獵戶座中屬性被定義爲:在頁面配置時設置,作用於當前組件,部署後不會發生變化的參數。
組件通過在其配置文件中聲明所需的屬性,來允許頁面搭建者設置屬性的值,並通過配置化引擎將頁面搭建者設置的值傳入組件。
屬性在組件配置文件中被表示爲一個對象數組,數組中的每個對象表示一個屬性。該對象的格式如下:
* 屬性名稱(name),表示將要傳入組件的props的名稱。
* 顯示文本(label),表示將要展示在獵戶座中的該參數的描述。
* 參數類型(type),表示將要展示在獵戶座中展示的配置該參數的方式,以及將要傳入組件的props的數據類型。
靜態參數的類型如下:
除了了上述所列出的三個通用屬性外,針對不同的參數類型,也會有一些特殊的屬性。
2.1 數值
數值類型可以設置單位(unit)。它的值是一個字符串。它將被用於展示在數字輸入框之後,告知用戶所輸入數值的單位。
2.2 值
值類型允許用戶自行選擇輸入內容的類型和內容的來源。
類型包括字符串(string)、數值(number)和布爾值(boolean)。
來源包括拼接字符串、固定值、接口下發參數、頁面傳參和環境變量。
2.3 單選項、多選項、下拉選框
單選項類型需要設置選項(options)。它的值是一個對象數組,數組中的每個對象需要包含選項值(value)和顯示文本(label)兩個字段。
2.4 顏色
顏色將被展示爲一個取色器。用戶可在取色器中選取一個顏色。最終傳入組件的數據爲用戶所選顏色的以#開頭的十六進制(hex)值。
2.5 動態數據項
動態數據項將被展示爲一個摺疊面板。用戶可動態的增加或刪除數據項,也可以通過拖拽調整數據項的順序。用戶展開摺疊面板後可以編輯每條數據項中的數據。
動態數據項類型需要設置字段列表(fields)。它的值是一個對象數組,它的值應該與屬性定義的格式一致。它將被用於展示和處理每條數據項中的數據。
動態數據項類型需要設置主字段(primary)。它的值是一個字符串,它的值應該是字段列表(fields)中任一字段的鍵值(key)。它將用於決定在展示數據項的摺疊面板被收起時所展示的標題(title)爲數據項中哪一個字段的值。
以數據卡片組件爲例:
其中,組件配置文件內容如下:
{
"static": [
{
"name": "title",
"label": "卡片名稱",
"type": "text"
},
{
"name": "value",
"label": "主數據",
"type": "number"
},
{
"name": "tips",
"label": "頁腳數據",
"type": "array",
"primary": "title",
"fields": [
{
"name": "title",
"label": "名稱",
"type": "text"
},
{
"name": "value",
"label": "數值",
"type": "number"
},
{ "name": "color",
"label": "顏色",
"type": "color"
}
]
}
]
}
可視化用戶界面根據組件配置文件內容,渲染配置化界面,供頁面搭建者進行配置。
配置化引擎根據組件配置文件,對頁面搭建者配置的內容進行處理,最終向組件傳入參數內容如下:
{
"title":"權益拉新",
"value":21818,
"tips":[
{
"title":"周同比",
"value":8.45,
"color":"#006000"
},
{
"title":"月同比",
"value":12.51,
"color":"#006000"
}
]
}
數據請求
通過代理數據請求後,組件本身不需關心接口下發的數據結構,只需要在其配置文件中聲明所需的數據格式、字段等信息,頁面搭建者可以在頁面配置文件中設置數據來源和字段映射關係。
對於數據請求,配置化引擎會通過同步、異步兩種方式向組件提供數據,以組件聲明需要名爲images的對象數組格式的數據爲例:
* 同步提供數據:配置化引擎會向組件提供一個名爲images的入參,該入參初始值爲空數組,待數據請求完成後替換爲目標數據。
* 異步提供數據:配置化引擎會向組件提供一個名爲getImages的異步方法(Promise),組件調用該方法後即可獲得格式正確的數據。
數據請求在配置文件中被表示爲一個對象數組,數組中的每個對象表示一個動態參數。該對象的格式如下:
* 參數名稱(name),用於生成供組件請求數據的方法的名稱。
* 顯示文本(label),表示將要展示在獵戶座中的該參數的描述。
* 數據格式(type),表示該參數所允許的數據格式。
動態參數的數據類型如下:
頁面配置者在頁面配置文件中配置接口請求信息、數據在接口返回中的字段,並根據組件配置文件中定義的數據結構進行字段映射等操作。最終由配置化引擎處理服務器下發數據,並將符合組件要求的數據傳入組件。
3.1 值、值的數組
單值、值的數組類型的數據請求不需額外配置項。
頁面搭建者可以在可視化用戶界面中選擇需要請求的接口、以及組件所需數據在接口響應數據中的字段。
配置化引擎請求用戶配置的接口,並從接口的響應中取出用戶配置的字段中的數據,將其傳入組件。
若接口下發數據格式爲:
{"code": 0, "result": {"erp": "niuxiaoguang", "name": "XXX"}}
數據來源處配置爲result.erp,則傳入組件的數據爲niuxiaoguang。
3.2 對象、對象的數組
對象、對象的數組類型的數據請求可以添加下列配置項。
3.2.1 字段
字段(fields)配置項表示組件所需數據中的字段。如新聞列表組件中,需要配置新聞的標題、發佈時間、鏈接等數據。
可視化用戶界面將會根據此配置項,額外的展示一個用於進行字段映射的區域,供用戶將接口響應數據中的字段映射到組件所需數據中的字段。
若組件配置文件如下:
{
"dynamic": [
{
"name": "data",
"label": "數據",
"type": "arrayObject",
"fields": [
{
"name": "title",
"label": "標題"
},
{
"name": "time",
"label": "發佈時間"
},
{
"name": "link",
"label": "鏈接"
}
]
}
]
}
若接口下發數據爲:
{"code": 0, "result": [
{
"name": "獵戶座-使用文檔",
"create_at": "2020年05月23日",
"url": "http://doc.jd.com/orion_doc/orion/introduce/"
},
{
"name": "獵戶座-新手指南",
"create_at": "2020年05月23日",
"url": "http://doc.jd.com/orion_doc/orion/guide/"
}
] }
數據來源處配置爲result,字段映射處配置爲:
* 標題:name
* 發佈時間:create_at
* 鏈接:url
則傳入組件的數據爲:
[
{
title: "獵戶座-使用文檔",
time: "2020年05月23日",
link: "http://doc.jd.com/orion_doc/orion/introduce/"
},
{
title: "獵戶座-新手指南",
time: "2020年05月23日",
link: "http://doc.jd.com/orion_doc/orion/guide/"
}
]
3.2.2 自定義字段
若組件所要求的數據結構中的字段是不確定的,例如圖表中的第三維度、表格中的列等,則可以使用自定義字段,允許用戶自行增、刪字段。
啓用自定義字段(customFields)配置項是一個布爾型參數,設置爲true時表示啓用自定義字段。
自定義字段選項(customFieldOptions)配置項是一個對象數組,其格式與屬性定義相同,用於爲自定義字段增加可選的擴展配置項信息,例如在折線圖組件中爲每條線分別配置顏色、粗細等信息。
若組件配置文件如下:
{
"dynamic": [
{
"name": "data",
"label": "數據",
"type": "arrayObject",
"fields": [
{
"name": "xAxis",
"label": "標題"
}
],
"customFields": true,
"customFieldOptions": [
{
"name": "color",
"label": "填充顏色",
"type": "color"
}
]
}
]
}
若接口下發數據爲:
{"code":0, result: [
{
"name": "首頁",
"uv": 93132,
"pv": 2392184
},
{
"name": "使用介紹",
"uv": 32913,
"pv": 923112
}
]}
數據來源處配置爲result,X座標數據處配置爲name,自定義字段處配置爲:
* 獨立訪客:uv
-
填充顏色:#1890FF
* 頁面瀏覽量:pv
-
填充顏色:#2A3F54
則傳入組件的數據爲:
[
{
name: "首頁",
獨立訪客: 93132,
頁面瀏覽量: 2392184
},
{
name: "使用介紹",
獨立訪客: 32913,
頁面瀏覽量: 923112
}
]
另外會通過dataCustomFields入參傳入:
{
獨立訪客: {
color: '#1890FF'
},
頁面瀏覽量: {
color: '#2A3F54'
}
}
3.2.3 其他配置項
其他配置項(options)是一個對象數組,其格式與屬性定義相同,用於提供與該動態參數相關的配置項。
層級關係
組件可以聲明其能夠在一個、多個或不定個數的位置插入子組件,來允許頁面搭建者在頁面配置文件中管理其子組件內容。
配置化引擎提供三種方式進行子自己的展示。
4.1 容器
容器可以用於在固定數量的位置插入子組件。
組件可以在配置文件的容器(children)部分配置需要放置子組件的容器。
容器在配置文件中被表示爲一個對象數組,數組中的每個對象表示一個容器。該對象的格式如下:
* 容器名稱(name),表示將要作爲props傳入組件的子組件的名稱。
以2欄組件爲例,組件關於子組件的配置項如下:
{
"children": [
{
"name": "column1"
},
{
"name": "column2"
}
]
}
在代碼中可通過下列代碼使用子組件:
import React from 'react'
import { Row, Col } from 'antd'
export default class Column2 extends React.Component {
render () {
return (
<Row>
<Col span={12}>{this.props.column1}</Col>
<Col span={12}>{this.props.column2}</Col>
</Row>
)
}
}
4.2 容器組
容器組可以用於在不定數量的位置插入子組件。
組件可以在配置文件的容器組(multipleChildren)部分配置需要放置子組件的容器。
容器組在配置文件中被表示爲一個對象數組,數組中的每個對象表示一組容器。該對象的格式如下:
* 容器組名稱(name),表示將要作爲props傳入組件的子組件的名稱。
* 顯示文本(label),表示將要展示在獵戶座中的該容器組的描述。
* 其他配置項(options)是一個對象數組,其格式與屬性定義相同,用於提供與容器組中各容器相關的配置項。
以多欄組件爲例,組件關於子組件的配置項如下:
{
"multipleChildren": [
{
"name": "columns",
"label": "列",
"options": [
{
"name": "width",
"label": "寬度比例",
"type": "number",
"unit": "/ 24"
}
]
}
]
}
則在獵戶座系統中,用戶可以看到下列配置信息:
在代碼中可通過下列代碼使用子組件:
import React from 'react'
import { Row, Col } from 'antd'
?
export default class ColumnCustom extends React.Component {
render() {
return (
<Row>
{
this.props.columns.map((column, index) => {
return (
<Col key={column.key} span={1*column.options.width}>
{column.children}
</Col>
)
})
}
</Row>
)
}
}
此時獵戶座引擎傳入組件的columns參數是一個數組,數組中的每個元素表示容器組中的每個容器。其中每個容器的信息包含:
* key,容器的key。
* children,容器的內容。
* options,容器的其他配置項。
跨組件交互
跨組件交互的定義分爲兩個方向。
5.1 觸發事件
組件可以觸發事件,例如點擊按鈕。
組件可以在配置文件中屬性定義部分使用操作類型聲名其可以觸發的事件。
頁面搭建者可以在頁面配置文件中聲明觸發事件的處理邏輯,包括:頁面跳轉、接口調用、彈出浮層和組件事件。其中組件事件即調用其他組件所註冊的處理事件。
此時配置化引擎會向組件提供一個方法,組件只需要在該事件觸發時調用該方法,並傳入配置文件中聲明提供的參數,而不必關心該事件的具體處理邏輯。
5.2 處理事件
組件也可以處理事件,例如刷新數據。此時配置化引擎會向組件提供一個名爲bindOperation的方法,組件需要在其被掛在到頁面中時調用該方法,將事件處理函數註冊到引擎當中。
組件可以在配置文件的操作配置(operation)部分配置組件可以處理的事件。
操作配置在配置文件中表示爲一個對象數組,數組中的每一個對象表示組件所支持的一個事件。該對象的格式如下:
* 操作名稱(name),表示組件所支持的事件的名稱,與組件在componentDidMount生命週期函數中向獵戶座引擎註冊事件時傳入的名稱一致。
* 顯示文本(label),表示將要展示在獵戶座系統中的該事件的描述。
例如某組件操作配置如下:
{
"operation": [
{
"name": "startLoading",
"label": "開始加載"
},
{
"name": "stopLoading",
"label": "結束加載"
}
]
}
則在該組件入口組件的componentDidMount生命週期函數中必須包含下列代碼:
componentDidMount () {
this.props.bindOperation('startLoading', this.startLoading)
this.props.bindOperation('stopLoading', this.stopLoading)
}
startLoading = () => {
console.log('startLoading')
// ...
}
stopLoading = () => {
console.log('stopLoading')
// ...
}
總結
以上就是我們在進行獵戶座系統開發過程中,對前端配置化引擎設計的一點經驗和總結。
後續我們將繼續與大家分享在獵戶座系統開發過程中遇到的業務邏輯配置化、第三方組件集成等問題的設計開發經驗。
感謝大家的瀏覽。