如何做一個看板搭建系統
如何做一個看板搭建系統
http://zoo.zhengcaiyun.cn/blog/article/buildingsystem
一、什麼是數據看板,數據看板有什麼用
在解釋數據看板概念之前,我們要先知道,什麼是數據可視化。
數據可視化被許多學科視爲與視覺傳達含義相同的現代概念。它涉及到數據的可視化表示的創建和研究。爲了清晰有效地傳遞信息,數據可視化使用統計圖形、圖表、信息圖表和其他工具。可以使用點、線或條對數字數據進行編碼,以便在視覺上傳達定量信息。有效的可視化可以幫助用戶分析和推理數據和證據。它使複雜的數據更容易理解和使用。- 維基百科
數據看板即是數據可視化的載體,通過合理的頁面佈局、效果設計來將可視化數據更好的展現。
個人認爲,數據看板的作用大致爲以下兩種:
1、掌握情況
通過數據呈現,決策者們能較爲清晰地掌握自己產品的運營情況。
2、問題解決
通過數據分析,能夠通過數據可視化,從動態數據中提煉出規律,發現不符合預期的部分並給出修改意見。
二、配置文件數據結構設計
假設有如下這樣的一個看板頁面,讓你用一份 json 文件來記錄組件的信息,類似,寬高、位置以及標題等等,試想一下你會怎麼設計 json 的數據結構(可以不用關心具體的值是什麼)。
這個問題並不是很難,相信大部分人都能設計一份自己的數據結構,比如我設計的結構就是下面這樣:
[
{
"type": 'line', // 類型
"id": 1, // 唯一標識
"data": [], // 數據存放
"title": "貨物銷售情況", // 組件標題
"layout": { x: 0, y: 0, w: 3, h: 2 }, // 頁面佈局信息(座標、寬高)
},
{
"type": 'bar',
"id": 2,
"data": [],
"title": "貨物留存情況",
"layout": { i: 'c', x: 3, y: 0, w: 3, h: 2 },
}
]
有了這樣一份數據結構之後,接下來,我們接下來就要開發組件,比如{type:line}
的配置項就去用 line 組件渲染,並且我們需要把數據還有一些其他配置項傳給組件。關於每個組件的開發,這裏就不深入探討了,需要考慮的就是你的內部組件要具有接受數據並處理配置的能力。
三、組件容器
現在組件有了,配置也有了,接下來就很簡單了,根組件直接循環遍歷下,就可以了,代碼如下:
<div>
{widgets.map(widget => {
const WidgetComp = getWidgetComp(widget.type);
return <WidgetComp config={widget} key={widget.id} />
})}
</div>
好了,大功告成,頁面渲染完成!
先彆着急,我們想想看,是不是每個組件,我們都需要去做同樣的工作,比如數據請求、佈局處理等等。
這時候,我們就需要在一個統一的地方去做這些同樣且重複的工作。這裏我想到了兩種方式:1、公共的 util 函數。2、給所有組件增加一層包裹容器。
第一種方法雖然可以滿足我們的要求,但是還是存在侷限性,那就是當我們想要在 dom 上做一些處理的時候,還是不可避免的會有重複代碼量,比如給組件添加點擊事件、設置 dom 的 style 屬性等。
我個人比較推薦第二種方法,也就是組件容器。一個組件容器大概長這樣:
const Widget = ({config}) => {
// 組件點擊事件
const handleClick=() => {/**/};
// 佈局處理
const handleLayout=() => {/**/};
// 獲取組件數據
const getWidgetData=() => {/**/};
// 其他的一些公共邏輯
.......
const WidgetComp = getWidgetComp(widget.type);
return (
return <WidgetComp config={widget} />
)
}
對應的根組件代碼可以改成這樣:
<div>
{widgets.map(widget => <Widget config={widget} key={widget.id} />)}
</div>
四、配置文件的編輯
上面的工作做完之後,我們可以做一個簡單的渲染了,但是既然是搭建系統,怎麼可能僅僅滿足於渲染呢?接下來,我們要考慮下,給搭建的用戶提供一個可以修改配置的地方。爲了統一口徑,我們把使用配置的地方,稱用戶側,修改配置的地方,稱編輯側。
一個編輯側可能長這樣:
大致分爲三個區域,組件商店、展示區域和配置區域,這裏我們先說右側的組件配置區域。
我們這裏拿 line 組件 舉例。假設,現在產品小姐姐給你提了第一個需求,需要這個 line 組件支持修改名稱。so easy!直接寫個 form 表單:
<Form form={form}>
<Item rules={[{ required: true, message: '請輸入' }]}>
<Input placeholder="請輸入" />
</Item>
</Form>
修改完之後,直接把新的名稱發給後端存起來就完事了。
一天後,產品小姐姐又提了另一個需求,bar 組件需要支持修改寬度。沒辦法,接着改,不能讓產品小姐姐看不起!於是你的代碼可能變成了這樣:
const renderForm = ({type}) => {
if(type === 'line') {
return (
<Item rules={[{ required: true, message: '請輸入' }]}>
<Input placeholder="請輸入" />
</Item>
)
} else if(type === 'bar') {
return (
<Item rules={[{ required: true, message: '請輸入' }]}>
<Input placeholder="請輸入" />
</Item>
)
}
}
return (<Form form={form}>
{renderForm()}
</Form>)
緊接着,產品小姐姐的需求與日增多,組件數量也越來越龐大,你的 if else 越寫越多....。所以我們需要一個統一的配置器,和一個統一的描述組件配置的 schema 文件。
先說組件的 schema 文件,每一個組件都配帶一個 schema 文件,schema 裏主要記錄當前組件支持修改的配置項(fields),和當前組件配置項的默認值(models)。類似這樣:
{
"fields": [{
"label": "名稱",
"type": "input",
"name": "name"
}],
"models": {
"name": "默認名稱"
}
}
當點擊一個組件的編輯按鈕時,根據組件類型獲取到對應的 schema 文件,將組件配置項默認值 models 和從後端拿到的數據,做一個 merge,將 merge 後的數據和 fieds 傳給配置器。
配置器要做的工作就是,根據 fileds 動態渲染表單,關於表單動態渲染,我們團隊有一篇不錯的文章《表單數據形式配置化設計》(https://juejin.cn/post/7119639489567260686), 大家有興趣可以參考下。
總結下編輯側的工作,第一步,拿到對應組件的 schema 文件,傳給配置器。第二步,配置器根據 fileds 動態渲染表單,並根據後端返回數據和 schema 中的默認數據,用作表單回顯。最後就是,搭建用戶修改配置項,再把修改後的數據發送給後端保存。
五、從遠程組件商店加載組件
以上已經完成了配置的產出、使用和修改。接下來我們再思考思考組件。我們現在的組件都是存在本地的,後期隨着組件數量的增多,頁面 js 文件肯定會越來越大,即使你使用了代碼分割,也不可避免會導致 build 後的包體積越來越大。
所以我們需要一個遠端存放組件的地方,也就是組件商店。
那麼商店既然是遠端的,那把這個商店建在哪裏呢?我們團隊的解決方案就是,把每個組件打上版本號上傳到靜態服務器上這樣,版本號的作用這裏先不管,這樣不管在編輯側還是用戶側,我們可以根據項目的配置數據遠程加載組件,關於遠程組件的加載方案,可以參考下我們團隊另一篇寫的不錯的文章《淺談低代碼平臺遠程組件加載方案》(https://juejin.cn/post/7127440050937151525)。
當然,一個組件商店不僅於此,我們目前只實現了一些基本功能,一個完整的組件商店功能包括:
-
組件線上編輯 (上傳) 模塊。
-
組件審覈模塊。
-
組件更新 / 發佈模塊。
-
組件管理 (上架 / 下架 / 刪除 / 下載 / 版本)。
六、靜態化
現在我們在編輯側修改提交,用戶側就能實時地得到修改後的配置了,對應的頁面刷新就會變化。
可是這樣會導致一個問題,假設以前的老版本 v1.0 需要修改到新版本 v2.0,並且工作量很大,需要兩天才能完成,那麼你第一天只能改一半,第二天早上你準備改另一半的時候,可能你們公司的投訴電話已經被打爆了,因爲,你的客戶早上打開頁面的時候,是你第一天只改了一半配置的頁面。
怎麼解決這個問題呢?答案是增加一個發佈操作,只有發佈過後,你修改的配置纔會在用戶側生效。
但是這樣還是會有問題,假設你在項目 B 修改了一個組件,該組件在項目 A 也用到了,那一旦該組件引發了 bug,項目 A,項目 B 都發出問題,如果是十幾個項目,那就會引起十幾個項目的問題。
基於此,我們需要思考一種方法,針對發佈過後的項目,即使對應組件有修改,也不會影響到它們,我們團隊使用的解決方案就是組件靜態化。
大致思路就是,發佈時,首先獲取對應的項目配置,根據項目配置獲取組件列表。然後根據列表,獲取遠程組件商店的 js 文件,將獲取到的 js 文件插入到對應的 html 模版中。最後,將拼裝好的 html 放到靜態服務器上。
這樣,後續用戶側只需要訪問靜態服務器上的 html 就可以了,即使組件也修改,已發佈的項目只要不重新發布,就不會影響已經發布的項目。
Tips: 進一步的話,我們可以把項目配置也做成靜態化,這樣,第一可以省去後端同學同步兩份不同環境數據的工作量,但對於前端來說,就是順手的事。第二,方便前端自己管理已發佈的配置數據。
但是如果組件修改,後續已發佈的項目也需要重新發布呢?怎麼儘量減少發佈風險呢?這個就需要做組件的版本管理和容器的版本管理了。
七、組件和容器的版本管理
前面我們在介紹組件商店的時候說到,上傳組件的時候,我們給對應組件打上了版本號,後續組件有修改的時候,修改過的組件,會被打上新的版本號。這樣,針對已發佈的項目,只要不更新對應組件的版本號,便不會影響對應的項目組件了。
那爲什麼要做容器的版本管理呢?前面我們介紹了組件容器的作用就是處理組件的通用邏輯,如果說組件的修改有可能會影響到其他項目,那麼組件容器的修改就是一定會影響到其他項目。所以容器的版本管理比組件的版本管理更加重要。思路其實跟組件差不多,我們可以把容器理解成一個特殊的組件,跟組件不同的是,這個組件不在配置文件解析出來的組件列表中。
八、組件之間的通信
在真實的搭建場景中,避免不了的就是,組件之間需要通信,比如,點擊組件 A,組件 B 需要做出對應的響應。我們在這裏借鑑的是【發佈 - 訂閱】。
我們設計一個事件調度中心,可以處理所有的事件註冊與事件響應。訂閱者發佈對應的事件,事件調度中心負責將事件放入事件池中。發佈者負責在對應的時機觸發對應事件,上面例子中,組件 A 就是發佈者,當組件 A 被點擊的時候,就是觸發事件的對應時機。事件在調度中心出發後,再將結果反饋給訂閱者。訂閱者拿到反饋結果做出行爲。
關於事件的配置描述的數據結構,形式有很多種,這裏貼一份我們目前項目中使用的數據結構:
export default {
publish:[ // 事件發佈
{
name: "電子賣場預警發佈/流程/onChange", // 發佈事件名
reflectName: "onChange" // 什麼行爲觸發該事件
}
],
subscribe: [ // 事件訂閱
{
list: ["電子賣場預警發佈/onChange"], // 訂閱的事件集合
action: "setClass", // 事件行爲,就是你訂閱的事件被觸發後,你要幹什麼
handler: "function parse(params) {↵ return {};↵ }" // 結合事件行爲與該事件的返回值,組件做出自身行爲
}
]
}
九、參考
1、《如何設計可視化搭建平臺的組件商店》(https://juejin.cn/post/6986824393653485605)
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/dk9tLrE_uNYYx5ZZRhtmag