天天看點

如何做一個看闆搭建系統

一、什麼是資料看闆,資料看闆有什麼用

在解釋資料看闆概念之前,我們要先知道,什麼是資料可視化。

資料可視化被許多學科視為與視覺傳達含義相同的現代概念。它涉及到資料的可視化表示的建立和研究。 為了清晰有效地傳遞資訊,資料可視化使用統計圖形、圖表、資訊圖表和其他工具。可以使用點、線或條對數字資料進行編碼,以便在視覺上傳達定量資訊。 有效的可視化可以幫助使用者分析和推理資料和證據。它使複雜的資料更容易了解和使用。 - 維基百科

資料看闆即是資料可視化的載體,通過合理的頁面布局、效果設計來将可視化資料更好的展現。

個人認為,資料看闆的作用大緻為以下兩種:

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 屬性等。

我個人比較推薦第二種方法,也就是元件容器。一個元件容器大概長這樣:

constWidget = ({config}) => {
  // 元件點選事件consthandleClick=() => {/**/};
  // 布局處理
  consthandleLayout=() => {/**/};
  // 擷取元件資料
  constgetWidgetData=() => {/**/};
  // 其他的一些公共邏輯
  .......
​
  constWidgetComp = getWidgetComp(widget.type);
  return (
    return<WidgetCompconfig={widget} />
  )
}
複制代碼
           

對應的根元件代碼可以改成這樣:

<div>
  {widgets.map(widget => <Widgetconfig={widget}key={widget.id} />)}
</div>複制代碼
           

四、配置檔案的編輯

上面的工作做完之後,我們可以做一個簡單的渲染了,但是既然是搭建系統,怎麼可能僅僅滿足于渲染呢?接下來,我們要考慮下,給搭建的使用者提供一個可以修改配置的地方。為了統一口徑,我們把使用配置的地方,稱使用者側,修改配置的地方,稱編輯側。

一個編輯側可能長這樣:

如何做一個看闆搭建系統

大緻分為三個區域,元件商店、展示區域和配置區域,這裡我們先說右側的元件配置區域。

我們這裡拿 line 元件 舉例。假設,現在産品小姐姐給你提了第一個需求,需要這個line元件支援修改名稱。so easy!直接寫個 form 表單:

<Form form={form}>
  <Item name="name" label="名稱" rules={[{ required: true, message: '請輸入' }]}>
    <Input placeholder="請輸入" />
  </Item>
</Form>
複制代碼
           

修改完之後,直接把新的名稱發給後端存起來就完事了。

一天後,産品小姐姐又提了另一個需求,bar 元件需要支援修改寬度。沒辦法,接着改,不能讓産品小姐姐看不起!于是你的代碼可能變成了這樣:

constrenderForm = ({type}) => {
  if(type === 'line') {
    return (
      <Itemname="name"label="名稱"rules={[{required:true, message: '請輸入' }]}>
        <Inputplaceholder="請輸入" />
      </Item>
    )
  } elseif(type === 'bar') {
    return (
      <Itemname="width"label="寬度"rules={[{required:true, message: '請輸入' }]}>
        <Inputplaceholder="請輸入" />
      </Item>
    )
  }
}
​
return (<Formform={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 動态渲染表單,關于表單動态渲染,我們團隊有一篇不錯的文章《表單資料形式配置化設計》,大家有興趣可以參考下。

總結下編輯側的工作,第一步,拿到對應元件的 schema 檔案,傳給修改器。第二步,修改器根據 fileds 動态渲染表單,并根據後端傳回資料和 schema 中的預設資料,用做表單回顯。最後就是,搭建使用者修改配置項,再把修改後的資料發送給後端儲存。

五、從遠端元件商店加載元件

以上已經完成了配置的産出、使用和修改。接下來我們再思考思考元件。我們現在的元件都是存在本地的,後期随着元件數量的增多,頁面js檔案肯定會越來越大,即使你使用了代碼分割,也不可避免會導緻build後的包體積越來越大。

是以我們需要一個遠端存放元件的地方,也就是元件商店。

那麼商店既然是遠端的,那把這個商店建在哪裡呢?我們團隊的解決方案就是,把每個元件打上版本号上傳到靜态伺服器上這樣,版本号的作用這裡先不管,這樣不管在編輯側還是使用者側,我們可以根據項目的配置資料遠端加載元件,關于遠端元件的加載方案,可以參考下我們團隊另一篇寫的不錯的文章《淺談低代碼平台遠端元件加載方案》。

當然,一個元件商店不僅于此,我們目前隻實作了一些基本功能,一個完整的元件商店功能包括:

  • 元件線上編輯(上傳)子產品。
  • 元件稽核子產品。
  • 元件更新/釋出子產品。
  • 元件管理(上架/下架/删除/下載下傳/版本)。

六、靜态化

現在我們在編輯側修改送出,使用者側就能實時的得到修改後的配置了,對應的頁面重新整理就會變化。

可是這樣會導緻一個問題,假設以前的老版本 v1.0 需要修改到新版本 v2.0,并且工作量很大,需要兩天才能完成,那麼你第一天隻能改一半,第二天早上你準備改另一半的時候,可能你們公司的投訴電話已經被打爆了,因為,你的客戶早上打開頁面的時候,是你第一天隻改了一半配置的頁面。

怎麼解決這個問題呢?答案是增加一個釋出操作,隻有釋出過後,你修改的配置才會在使用者側生效。

但是這樣還是會有問題,假設你在項目 B 修改了一個元件,該元件在項目A也用到了,那一旦該元件引發了 bug,項目 A,項目 B 都發出問題,如果是十幾個項目,那就會引起十幾個項目的問題。

基于此,我們需要思考一種方法,針對釋出過後的項目,即使對應元件有修改,也不會影響到它們,我們團隊使用的解決方案就是元件靜态化。

大緻思路就是,釋出時,首先擷取對應的項目配置,根據項目配置擷取元件清單。然後根據清單,擷取遠端元件商店的 js 檔案,将擷取到的 js 檔案插入到對應的 html 模版中。最後,将拼裝好的 html 放到靜态伺服器上。

這樣,後續使用者側隻需要通路靜态伺服器上的 html 就可以了,即使元件也修改,已釋出的項目隻要不重新釋出,就不會影響已經釋出的項目。

Tips: 進一步的話,我們可以把項目配置也做成靜态化,這樣,第一可以省去後端同學同步兩份不同環境資料的工作量,但對于前端來說,就是順手的事。第二,友善前端自己管理已釋出的配置資料。

但是如果元件修改,後續已釋出的項目也需要重新釋出呢?怎麼盡量減少釋出風險呢?這個就需要做元件的版本管理和容器的版本管理了。

七、元件和容器的版本管理

前面我們在介紹元件商店的時候說到,上傳元件的時候,我們給對應元件打上了版本号,後續元件有修改的時候,修改過的元件,會被打上新的版本号。這樣,針對已釋出的項目,隻要不更新對應元件的版本号,便不會影響對應的項目元件了。

那為什麼要做容器的版本管理呢?前面我們介紹了元件容器的作用就是處理元件的通用邏輯,如果說元件的修改有可能會影響到其他項目,那麼元件容器的修改就是一定會影響到其他項目。是以容器的版本管理比元件的版本管理更加重要。思路其實跟元件差不多,我們可以把容器了解成一個特殊的元件,跟元件不同的是,這個元件不在配置檔案解析出來的元件清單中。

八、元件之間的通信

在真實的搭建場景中,避免不了的就是,元件之間需要通信,比如,點選元件 A,元件 B 需要做出對應的響應。我們在這裡借鑒的是【釋出-訂閱】。

我們設計一個事件排程中心,可以處理所有的事件注冊與事件響應。訂閱者釋出對應的事件,事件排程中心負責将事件放入事件池中。釋出者負責在對應的時機觸發對應事件,上面例子中,元件 A 就是釋出者,當元件 A 被點選的時候,就是觸發事件的對應時機。事件在排程中心出發後,再将結果回報給訂閱者。訂閱者拿到回報結果做出行為。

如何做一個看闆搭建系統

關于事件的配置描述的資料結構,形式有很多種,這裡貼一份我們目前項目中使用的資料結構:

exportdefault {
  publish:[ // 事件釋出
    {
      name: "電子賣場預警釋出/流程/onChange", // 釋出事件名
      reflectName: "onChange"// 什麼行為觸發該事件
    }
  ],
  subscribe: [  // 事件訂閱
    {
      list: ["電子賣場預警釋出/onChange"], // 訂閱的事件集合
      action: "setClass", // 事件行為,就是你訂閱的事件被觸發後,你要幹什麼
      handler: "function parse(params) {↵ return {};↵ }"// 結合事件行為與該事件的傳回值,元件做出自身行為
    }
  ]
}
           

作者:政采雲前端團隊

連結: https://juejin.cn/post/7197433202171854885

繼續閱讀