BI 數(shù)據(jù)可視化平臺(tái)建設(shè)(2)—篩選器組件升級(jí)實(shí)踐
一、引言
BI產(chǎn)品通常包含大量復(fù)雜的數(shù)據(jù)信息,需要對(duì)其進(jìn)行快速和準(zhǔn)確的處理和分析。篩選器可以幫助BI產(chǎn)品的用戶快速地定位所需信息,并從海量數(shù)據(jù)中篩選出有用的數(shù)據(jù),以便進(jìn)行深入的分析和決策。敏捷BI作為公司內(nèi)部用戶數(shù)最多的可視化平臺(tái),隨著平臺(tái)的業(yè)務(wù)增長和版本迭代,其篩選器功能也越來越豐富和完善,舊的設(shè)計(jì)架構(gòu)也顯得越來越臃腫且難以維護(hù),為了提高篩選器使用的穩(wěn)定性和降低后續(xù)迭代維護(hù)成本,篩選器的架構(gòu)升級(jí)已經(jīng)不可避免了,本文主要給大家介紹一下篩選器組件的架構(gòu)升級(jí)實(shí)踐經(jīng)驗(yàn)。
二、前期設(shè)計(jì)
2.1 組件選型
前期篩選器組件的職責(zé)和交互比較簡單,主要是對(duì)圖表數(shù)據(jù)進(jìn)行單向的數(shù)據(jù)過濾,并沒有應(yīng)用到其他的業(yè)務(wù)場(chǎng)景中,所以前期的組件設(shè)計(jì)主要以 業(yè)務(wù)組件 的思路進(jìn)行開發(fā)實(shí)現(xiàn)。
2.2 組件分類
組件類型主要可以分為業(yè)務(wù)組件和通用組件兩種,它們?cè)诮M件的狀態(tài)管理和界面渲染的設(shè)計(jì)和實(shí)現(xiàn)上是完全不同的。
但無論是業(yè)務(wù)組件或者通用組件都具備組件本質(zhì)所包含的三個(gè)性質(zhì) 擴(kuò)展、通用、健壯:
- 擴(kuò)展性:在原有組件基礎(chǔ)上可 二次封裝 擴(kuò)展成新的組件符合設(shè)計(jì)的開閉原則。
- 通用性:根據(jù)組件接受的參數(shù)和組件中與業(yè)務(wù)的解耦比來衡量組件的通用性,并不是通用性占比100%的組件就是最好的組件,需要根據(jù) 不同的場(chǎng)景 分析。
- 健壯性:避免組件中參數(shù)處理和函數(shù)執(zhí)行過程可能出現(xiàn)的奔潰和錯(cuò)誤導(dǎo)致程序的直接掛斷,單測(cè)以對(duì)組件內(nèi)部 做好邊界處理,異常錯(cuò)誤的捕獲來衡量這一標(biāo)準(zhǔn)。
因此兩種組件類型沒有絕對(duì)優(yōu)劣之分,重要的是在保證組件設(shè)計(jì)的基本原則不變的情況下,根據(jù)不同的業(yè)務(wù)場(chǎng)景和需求選擇合適的類型 。無論哪種組件,隨著不斷擴(kuò)展,使其通用性提升,必然就會(huì)降低組件的易用性質(zhì);而不斷豐富一個(gè)組件,也會(huì)導(dǎo)致其組件代碼過長、組件使命不單一、不易讀、不易維護(hù);因此組件設(shè)計(jì)除了要保證組件的基本性質(zhì),還要通過明確組件職責(zé)、組件拆分粒度以及良好的代碼結(jié)構(gòu)和Api設(shè)計(jì)規(guī)范對(duì)組件的迭代進(jìn)行約束,避免代碼邏輯的過度疊加和膨脹。
2.3 背景痛點(diǎn)
舊版篩選器組件設(shè)計(jì)存在維護(hù)成本高且問題BUG多等問題,主要由兩個(gè)原因造成,第一個(gè)是業(yè)務(wù)發(fā)展,隨著業(yè)務(wù)的快速增長,篩選器組件的功能也越來越豐富和完善,由原來的單一功能升級(jí)成可以支持?jǐn)?shù)據(jù)預(yù)警、個(gè)性化分析等多種業(yè)務(wù)場(chǎng)景的核心模塊;第二個(gè)是缺乏規(guī)范約束,主要是缺少良好的代碼結(jié)構(gòu)和清晰的組件職責(zé)等規(guī)范約束,導(dǎo)致業(yè)務(wù)邏輯過度疊加,粒度拆分不合理,文件多,且文件名不規(guī)范。最終導(dǎo)致了篩選器組件的穩(wěn)定性越發(fā)不可控。
由于前期設(shè)計(jì)不合理和缺乏規(guī)范約束,篩選器組件經(jīng)過了一段時(shí)間的野蠻式迭代擴(kuò)展帶來了以下的痛點(diǎn)問題:
- 重復(fù)代碼多,復(fù)用性差:相同的業(yè)務(wù)邏輯需要維護(hù)多份代碼,導(dǎo)致出現(xiàn)bug的概率大大增加,后期維護(hù)成本增加;
- 業(yè)務(wù)耦合度高,缺乏設(shè)計(jì)模式進(jìn)行管理:更新迭代過程中處理邏輯需要兼容多種場(chǎng)景代碼越來越復(fù)雜,導(dǎo)致問題難以跟蹤,難以定位問題意味著你可能需要花大部分的時(shí)間處理問題;
- 編碼風(fēng)格不一致,維護(hù)成本高: 項(xiàng)目主要技術(shù)棧是Vue,但是代碼風(fēng)格有大部分格使用的React的jsx形式進(jìn)行開發(fā);項(xiàng)目存在多人維護(hù),個(gè)人技術(shù)參差不齊;導(dǎo)致后續(xù)學(xué)習(xí)成本增加;
- 組件嵌套層級(jí)深,存在雙向數(shù)據(jù)流:不符合Vue 單向數(shù)據(jù)流狀態(tài)管理理念,無法追蹤局部狀態(tài)的變化,增加了出錯(cuò)時(shí) debug 的難度,經(jīng)常出現(xiàn)修改一個(gè)模塊bug而引起其他模塊bug的情況。
三、新版架構(gòu)設(shè)計(jì)
3.1 設(shè)計(jì)思路
舊版的組件隨著業(yè)務(wù)發(fā)展迭代,已經(jīng)混雜著大量的業(yè)務(wù)邏輯,組件耦合嚴(yán)重,職責(zé)也越發(fā)不清晰,因此為了合理的劃分組件職責(zé)和清晰代碼結(jié)構(gòu),新的架構(gòu)設(shè)計(jì)將基于 通用組件 的設(shè)計(jì)思路,將篩選器組件抽離出BI業(yè)務(wù);從BI項(xiàng)目的架構(gòu)、技術(shù)選型、文檔使用等多個(gè)方面進(jìn)行考慮,在原來的基礎(chǔ)上改造太復(fù)雜,可行性低,所以搭建了一個(gè)新的項(xiàng)目,將之前所有的篩選器組件遷移到新項(xiàng)目上,穩(wěn)定后替換BI項(xiàng)目上所有舊版篩選器組件,后續(xù)統(tǒng)一只需維護(hù)一個(gè)組件庫(bi-filters)。
3.2 實(shí)現(xiàn)方案
篩選器組件庫(bi-filters)主要 基于Vue CLI 的 開發(fā)/構(gòu)建目標(biāo)/庫 能力以及 Lerna 包管理工具 進(jìn)行設(shè)計(jì)開發(fā),這種組件庫設(shè)計(jì)集成了以下特點(diǎn):
- 按需引入:每個(gè)UI組件都是一個(gè)npm包,多語言、工具和樣式都是自成體系的npm包,可被業(yè)務(wù)或UI組件靈活引用,同時(shí)天然支持按需加載。
- 配置簡單:如果需要進(jìn)行構(gòu)建處理,那么每個(gè)npm包可單獨(dú)進(jìn)行構(gòu)建配置,配置變得更加簡單。結(jié)合Vue CLI的構(gòu)件庫能力,對(duì)于簡單UI組件的構(gòu)建幾乎可以做到webpack零配置。
- 獨(dú)立部署:組件庫的版本迭代可以更快,不需要進(jìn)行整體構(gòu)建,每個(gè)組件可單獨(dú)快速發(fā)布。
1. 利用 Lerna工具進(jìn)行多包管理,快速對(duì)組件庫進(jìn)行版本發(fā)布
組件庫目錄結(jié)構(gòu):
圖片
圖片
2. 組件設(shè)計(jì)和實(shí)現(xiàn)
參考 裝飾器設(shè)計(jì)模式,對(duì)組件進(jìn)行抽象設(shè)計(jì),從而達(dá)到業(yè)務(wù)狀態(tài)與 UI 狀態(tài)隔離,UI 狀態(tài)與交互呈現(xiàn)隔離的目的。具體實(shí)現(xiàn)是先按功能將組件拆成展示層,邏輯層,容器層,達(dá)到組件分層可復(fù)用。再通過
$attrs/$listeners對(duì)antd組件進(jìn)行二次封裝,抽離成在篩選器組件庫內(nèi)的公共組件,達(dá)到交互可組合。最終使得組件邊界清晰,符合設(shè)計(jì)規(guī)范中提到的開閉原則、單一職責(zé)原則、里氏替換原則。
以文本下拉篩選器組件(TextDropDownFilter)實(shí)現(xiàn)為例:
(1)按功能將組件拆分成 容器層、邏輯層(搜索框邏輯層、 下拉列表邏輯層 )、展示層(搜索框展示層、下拉列表展示層):
圖片
圖片
(2)BI項(xiàng)目中使用 :引入篩選器組件后, 在BI應(yīng)用層處理業(yè)務(wù)場(chǎng)景,將處理業(yè)務(wù)后的狀態(tài)信息通過 Vue 插槽(Slots)的方式傳遞給底層的篩選器組件 。
<!-- page.vue一 -->
<TextDropDownFilter>
<template #addonSearchAfter>
<!-- 業(yè)務(wù)場(chǎng)景一 -->
<a-tooltip v-if="xxx">
<BIIcon type="icon-jilian" class="btn-jilian" v-show="xxx"></BIIcon>
</a-tooltip>
<!-- 業(yè)務(wù)場(chǎng)景二 -->
<a-tooltip v-if="xxx">
<AIcon type="warning" theme="filled" class="btn-warning"></AIcon>
</a-tooltip>
</template>
</TextDropDownFilter>
(3)搜索框邏輯層:接收業(yè)務(wù)處理后的狀態(tài),進(jìn)行不同的UI組合展示
<!-- SearchHandler.vue一 -->
<template>
<div class="bd-search" :class="{ 'active': inputActive }">
<!-- 基礎(chǔ)搜索框組件 一 -->
<Search
v-bind="$attrs"
:searchValue="searchValue"
:placeholder="placeholder"
@searchItem="handleSearchItem"
@pressEnter="handlePressEnter"
@focus="handleFocus"
@blur="handleBlur"
>
</Search>
<!-- 業(yè)務(wù)層傳入的UI 一 -->
<slot name="addonSearchAfter"></slot>
</div>
</template>
(4)搜索框展示層:由 antd 基礎(chǔ)組件組成,提供交互單一且可復(fù)用的UI組件
<!-- Search.vue一 -->
<template>
<AInput
class="common-search-input"
:placeholder="placeholder"
:value="searchValue"
allow-clear
@change="change"
v-on="$listeners"
@pressEnter="$emit('pressEnter', $event)"
>
<AIcon slot="prefix" type="search" />
</AInput>
</template>
3. 最后利用 Vue CLI 的構(gòu)建庫功能,對(duì)不同類型的篩選器組件進(jìn)行單獨(dú)構(gòu)建打包
vue cli 的構(gòu)建庫能力可以通過 --target 選項(xiàng)指定不同的構(gòu)建目標(biāo)。它允許你將相同的源代碼根據(jù)不同的用例生成不同的構(gòu)建。
在組件庫項(xiàng)目的 packages 目錄下,每一個(gè)篩選器組件的目錄下都需要?jiǎng)?chuàng)建 package.json文件,用于組件的構(gòu)建信息配置:
{
"name": "@bigdata/TextDropDownFilter", //包名
"version": "0.0.0", // 版本號(hào)
"private": false, // 為true時(shí)不會(huì)被發(fā)布
"main": "dist/編譯文件名.umd.min.js",
"scripts": {
"build": "vue-cli-service build --target lib --name 編譯文件名 --dest dist ./index.js",
"lint": "",
"test:unit": ""
},
"files": [
"dist"
],
"author": "",
"license": "ISC",
"description": ""
}
四、效果收益
1. BI項(xiàng)目整體代碼量減少,組件目錄結(jié)構(gòu)清晰,只需要專注維護(hù)業(yè)務(wù)邏輯
2. BI業(yè)務(wù)抽離后,篩選器組件可進(jìn)行獨(dú)立維護(hù)迭代,減少代碼耦合,只需專注功能交互和性能優(yōu)化,提高組件穩(wěn)定性。
五、總結(jié)
從上述的升級(jí)過程可以看出,組件的抽象與抽象粒度是沒有一成不變的統(tǒng)一標(biāo)準(zhǔn),也沒有對(duì)與錯(cuò)。組件的設(shè)計(jì)更多的應(yīng)該去關(guān)注如何適配不同的業(yè)務(wù)場(chǎng)景和需求要求,追求更多的是“適合”。有的時(shí)候,同樣的業(yè)務(wù)場(chǎng)景,組件粒度的標(biāo)準(zhǔn)也會(huì)隨業(yè)務(wù)場(chǎng)景變化而變化,甚至可能隨場(chǎng)景增加而持續(xù)重構(gòu),因此為了代碼更好的維護(hù)和分層,以及避免代碼邏輯的過度疊加和膨脹,必須制定一些組件抽象的規(guī)范加以約束??偟膩碚f,組件開發(fā)的方法論可能是相對(duì)中立和普適的,但組件庫的整體建設(shè)方案,與所在的行業(yè)和業(yè)務(wù)有很大的關(guān)系。不同的行業(yè)領(lǐng)域,對(duì)交互展現(xiàn)的掌控程度是不一樣的,因此設(shè)計(jì)組件庫方案的時(shí)候,應(yīng)該優(yōu)先從產(chǎn)品項(xiàng)目的集成關(guān)系角度出發(fā)看待問題,這樣可以保證業(yè)務(wù)的拓展和可用性盡可能不被技術(shù)方案限制。