如何做一個看板搭建系統

如何做一個看板搭建系統

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