作者:vivo 網際網路巨量資料團隊 - Zhu Jianchen
本文是vivo網際網路巨量資料團隊《BI資料視覺化平臺建設》系列文章第1篇 - 交叉表元件。
交叉表在資料分析裡應用廣泛,通過本文,你將瞭解到:
交叉表的基本概念,以及BI視覺化平臺常見術語。
我們的表格類元件的演化過程,以及如何通過技術調研和優化實現巨量資料量下渲染效能,一步一步從原先的~10s降低到3~4s。
交叉表的一些特定場景,提供了一些技術實現簡易描述,對這些場景有一些宏觀認識。
Worker,虛擬捲動,微應用等關鍵技術的實現細節。
表格和表單在前端裡面是最複雜的兩類需求,在BI工具平臺上,這2類元件需求更多,並且需要實現一些特有的互動展示。目前在敏捷BI平臺上進行報表設定,表格類元件的使用佔比達到了1/3,在視覺化元件庫裡使用範圍很廣。為了滿足不同的資料分析場景,表格元件主要分為分組表、交叉表、明細表三種型別,其中又以交叉表功能最為豐富強大。隨著敏捷BI的業務的發展,交叉表元件也經歷了多次設計改版以支援高效能的資料渲染和個性化的展示設定。本文主要通過交叉表元件的升級實踐給大家講解一下如何設計開發高效能的表格元件。
術語註解
【敏捷BI】
專為 vivo 生態使用者量身打造的 自助式 BI 平臺,提供從資料接入、資料準備、到資料分析、視覺化應用、資料管理的一站式資料解決方案,同Quick BI,FineBI。
【圖表型別】
圖表是資料視覺化表示的特殊方式。表示資料的方法有很多,如使用不同的符號、形狀和排列,我們把這些稱之為圖表的型別。一些圖表型別你比較熟悉,如條形圖、餅圖、折線圖,但其他型別你可能就很少見了,如桑基圖、樹圖、等值線圖的地圖。
【互動方式】
互動式視覺化允許您修改,操作和探索計算機顯示的資料。絕大多數互動式視覺化系統在計算機網路上,但越來越多出現在平板電腦和智慧手機上。相比之下,靜態視覺化只顯示單一的、非互動資料,它通常是為了列印和在螢幕上顯示。
【度量值】
表示數值的規模和範圍。度量通常以間隔表示(10、20、30等等),代表度數位的單位,如價格、距離、年,或百分比。
【指標】
同度量,表示具體某項值,單個值本身沒有任何業務意義,一般需要對應的指標口徑解釋,才會具有業務價值。
交叉表(Cross Tabulations)是一種常用的由 行、列、彙總欄位 三個元素組成分類彙總表格。利用交叉表查詢資料非常直觀明瞭,在進行資料分析中也被廣泛應用。這裡牽涉到另外一個概念即分組報表,分組報表是所有報表當中最普通,最常見的報表型別,也是所有報表工具都支援的一種報表格式。從一般概念上來講,分組報表就是隻有縱向的分組,傳統的分組報表製作方式是把報表劃分為條帶狀,使用者根據一個資料繫結嚮導指定分組,彙總欄位,生成標準的分組報表。交叉表有多列查詢能力、分類彙總、多角度排序、互動式分析等特性。
為了提高交叉表的資料渲染效能和功能擴充套件能力,敏捷BI的表格元件經歷了三次的設計升級。最開始用的jQuery拼接表格方式實現,隨著元件化方案的推行,採用了元件化的方式實現升級,隨著業務的發展,用多維度、多指標交叉分析場景越來越多了,尤其是通過交叉表進行分析時,巨量資料量出現了渲染崩潰等問題,所以我們最後通過微前端方式實現。 下面我們從開發難度,效能,功能擴充套件性,學習成本等方面的調研來講解對底層表格的升級實踐。
敏捷BI平臺第一版表格,技術棧是基於jQuery+DIV的方式實現的。表格拼接屬於jQuery時代的常見開發風格,這種方式,程式碼可維護性會非常差,很容易會出現標籤不匹配的情況,不帶縮排,偵錯起來也比較費勁。這個版本的表格元件支援的業務場景主要是資料的基本展現,無法滿足使用者對錶格的資料分析的需求。
架構設計
// 簡單的拼接程式碼demo
function createTable() {
var data = new Array();
data.push('<table border=1><tbody>');
for (var i = 0; i < 2000; i++) {
data.push('<tr>');
for (var j = 0; j < 5; j++) {
data.push('<td>' + i + ',' + j + '</td>');
}
data.push('</tr>');
}
data.push('</tbody><table>');
document.getElementById('table1').innerHTML = data.join('');
}
隨著系統整體架構升級,前後端分離的推進,我們從原生的table元件遷移到Vue元件化上,開發了V2版表格元件。 平臺的整體架構全面遷移到vue+ant-design-vue上面。
1. 功能拓展
鑑於ant-design-vue上正好有table元件,對此我們對比了antd的table元件和element的table元件。2 種表格對比來看,ant-design-vue參照ant.design的React版開發出來,設定相對element更豐富,考慮到本身複雜場景支援性,更適合深度客製化,最終選擇了ant.design的vue版本。
V2版的表格主要支援這幾類場景設定(條件格式,合計行/列,單元格/行樣式/內容客製化等):
業務場景&具體實現
(1)資料展示
整體就是根據不同情況設定不同的column的欄位,另外為了達到點選互動下,能夠獲取業務的資料,需要在column上掛一些冗餘資料,這樣會讓column的資料資訊很龐大。
columns是一個tree結構,這裡採用的是dfs遍歷,depth標識層級,item, itemType就是冗餘的資料資訊,在處理業務的時候會用到。
(2)資料排序
(3)資料過濾
(4)單元格自定義渲染
(5)多級表頭客製化
這個實現難點主要在於把已有的列如何放到新增的表頭裡,保持樹形children結構具體實現程式碼也比較複雜,總共80行。
(6)條件格式渲染(條形圖,熱力圖)
根據設定的條件,客製化表格內單元格內容的樣式
(7)合計行/列設定
新增合計列和行,內建min,max,avg,sum表示式,支援自定義簡易欄位表示式運算這個功能難點在於合計列與行交叉的場景,也就是如何計算合計列的合計行。
2. 架構設計
3. 渲染優化
這個階段的交叉表,在功能上已經能夠滿足絕大多數分析場景,但是一些資料量大的表格反饋渲染白屏時間過長,經常會出現瀏覽器崩潰,表格的效能面臨新的挑戰。另外表格在渲染時,CPU會佔滿,導致其他圖表也會卡住等待,形成假死的現象。我們通過分析巨量資料表格渲染流程,發現有30%的時間會花銷在資料適配,因此我們思考能不能把資料計算部分隔離出來,計算的時候,不阻塞渲染主程序,這樣的話,瀏覽器渲染就可以處理其他的渲染任務。在做效能優化調研時,我們引入了service worker,worker在處理cpu密集型任務有獨特的優勢,所以我們把資料預處理的過程交給了Worker。之前沒有使用worker時,我們前端邏輯會處理很多資料初始化和計算的操作,對於一個資料量很大的表格,會導致渲染卡頓2~3s,有些個別情況會導致瀏覽器崩潰的現象。
Worker原理和定義:
W3C 組織早在 2014 年 5 月就提出過 Service Worker 這樣的一個 HTML5 API ,主要用來做持久的離線快取。service worker是瀏覽器的一個高階特性,本質是一個web worker,是獨立於網頁執行的指令碼。
web worker這個api被造出來時,就是為了解放主執行緒。因為,瀏覽器中的JavaScript都是執行在單一個執行緒上,隨著web業務變得越來越複雜,js中耗時間、耗資源的運算過程則會導致各種程度的效能問題。
而web worker由於獨立於主執行緒,則可以將一些複雜的邏輯交由它來去做,完成後再通過postMessage的方法告訴主執行緒。service worker則是web worker的升級版本,相較於後者,前者擁有了持久離線快取的能力。
在開發V2表格時,我們意識到資料處理部分不應該交給前端,列拼接上摻雜了太多的業務場景處理,另外渲染效能和崩潰問題急需解決,對此我們進行了V3版本迭代,提前對錶格版本進行了技術升級,為之後的新一批列彙總行彙總,分組小計等高階交叉分析需求做好技術儲備。
1. 技術選型
我們對比了react,vue及canvas生態有代表性的表格元件。綜合三者優劣勢最終確定了基於react的table元件。
S2:https://github.com/antvis/S2
ali-react-table:https://github.com/alibaba/ali-react-table
vxe-table:https://github.com/x-extends/vxe-table/tree/v2
vxe-table設計初衷是解決單元格編輯的問題,主要用於大量增刪改查的場景,效能不是它唯一的目標;S2是建立在電子試算表需求上的,對篩選、排序、搜尋、複製、框選、聚合分析都有訴求。
同時需要在巨量資料量下保持高效能,解決之前商業軟體版本實現的效能問題和拓展性問題,所以它覆蓋的場景更全更復雜,但是它的缺點就是客製化型不強,不太適合我們自身的業務。
所以最終我們選擇了ali-react-table,它本身體積小,在基礎能力都滿足的情況下,擴充套件新功能也很容易,而且在巨量資料量渲染下有高效能的優勢。
2. 架構設計
後端介面返回資料和設定部分,基於渲染模型:左樹 + 上樹 => 表格,根據設定生成左樹leftTree和上樹topTree,構造資料來源,參照了ant-design的Table元件資料來源構造的流程,與自身的pipeline外掛機制結合,實現了表格的互動操作(排序,篩選,分頁)。
由於本專案裡接入了微前端架構,採用了loadApp的方式實現了異構應用混合開發:
運作流程圖如下:
3. 升級實踐
(1)架構升級:
(2)底層渲染:
虛擬捲動:長列表渲染受制於瀏覽器本身限制,在大量DOM下,會達到瀏覽器本身的渲染瓶頸,在這種情況下,虛擬捲動可以解決這種渲染問題,它是一種按需渲染的理念的體現。所以虛擬列表是一種根據捲動容器元素的可視區域來渲染長列表資料中某一個部分資料的技術。
大致原理如下圖:
我們發現長列表在展示時,使用者只會關注可視區域,其他非可視區域部分,我們可以把已經渲染的DOM銷燬,不需要立即渲染的DOM延後。所以優化策略就是隻渲染可見區域的內容。
在捲動事件觸發後,根據捲動 Offset 調整相應渲染的內容即可。在使用者看來,還是一個完整的長列表。這種懶載入的方式,和早期頁面圖片資源懶載入和非必須資源非同步載入屬於同一種思路。
在新版版本里,ali-react-table自帶了虛擬捲動的特性,在大列表下,框架會自動開啟,可以明顯提升表格渲染效能和捲動的效能。
1. Quick BI
① 架構設計
② 技術實現
使用原生div和flex佈局,不使用原生table表格
列寬,固定列,固定表頭等表格不好實現的問題,都易實現,渲染效能也較好
有2個版本的表格,舊版表格使用table,在這種情況下,效能,複雜互動,分組都存在瓶頸,這一點和我們類似,新老版本的表格同時線上上應用
虛擬捲動支援橫向,縱向捲動
③ 優劣勢
ali-react-table不維護了,原始碼不太複雜,可以二次迭代開發;基本滿足交叉表所有功能;巨量資料量下渲染高效能
介面資料略冗餘
④ 備註
資料結構明確行維、列維、指標列資料;
資料彙總和小計是存放在後端
2. 敏捷BI
① 架構設計
② 技術實現
使用table佈局
使用position:sticky實現固定列;固定表頭使用獨立的單表頭表格模擬,這裡需要強制table設定列寬,保證列對齊
支援橫向,縱向虛擬捲動,在10w列下依然可以正常渲染
在ali-react-table基礎上擴充套件了按維度合併,表頭篩選等feature
③ 優劣勢
flex佈局靈活,不受表格本身佈局限制,易實現固定列和表頭,列寬;canvas開發成本較高,bug不好偵錯
介面資料更精簡
④ 備註
資料結構不明確,需要對二維陣列轉換,存在一定的預處理邏輯;
資料結構存在冗餘現象
以實際測試為準
1. Quick BI
業務場景
(1)欄位設定
行:資料集的維度欄位拖拽到行選擇區
列:資料集裡的維度或者度量欄位拖拽到列選擇區
過濾器:資料集欄位拖拽到過濾器選擇區,對欄位進行篩選
(2)樣式設定
標題與卡片:設定標題樣式
備註和章節附註:設定圖表備註和章節附註內容
元件容器:設定內邊距和背景色,圓角
展示型設定:設定主題,表頭樣式,內容樣式,凍結列,序號等設定
功能型設定:條件格式設定,針對欄位滿足特定條件下突出顯示設定的樣式
總計設定:支援列彙總和行彙總,行總計和行小計,列總計和列小計
(3)高階設定
聯動:圖表裡的欄位與其他圖示關聯
跳轉:圖表欄位跳轉傳值
技術實現
實現使用原生,未使用第三方庫
自定義主題使用主色編輯
拖拽方式互動
2. 網易有數
業務場景
沒有複雜的交叉表場景,只支援普通明細表
設定方式主要包括主題,表頭,內容的字型樣式,背景,對齊等樣式
支援下鑽,欄位跳轉
資料集欄位支援維度層級和組的概念
沒有虛擬捲動
技術實現
內部使用table表格實現
主題設定支援上傳主題json檔案
3. 敏捷BI
(1)欄位設定
行維:資料集維度欄位放置區
列維:資料集維度欄位放置區
指標:資料集指標欄位
(2)圖表屬性和圖表樣式設定
支援條件格式,自定義程式碼樣式嵌入,主題設定
(3)欄位過濾
使用欄位過濾資料
技術實現
最開始使用smooth-dnd庫,來實現從資料集欄位拖拽到行列、指標區
因為smooth-dnd有效能問題,不再維護等問題,就廢棄掉了,使用原生的拖拽實現
主題使用線上程式碼編輯主題,基於codemirror線上程式碼,接入css variables,實時應用,不需要重新整理
應用場景① :表頭篩選
程式碼實現:
應用場景②:按維度合併
程式碼實現:
1. Quick BI
(1)資料量級 <50列
介面耗時300ms 介面大小<5kb
渲染耗時 < 1s
注:資料量不是很大的情況下,資料載入忽略不計,合入到資料渲染時間,差別不大
(2)資料量級 ≥ 200列
介面耗時1.88s 介面大小<10kb
表格渲染 < 3s
注:資料量很大的情況下,資料載入需要單獨計入時間
(3)資料量級 > 1W列
極端情況下,表格渲染崩潰
2. 敏捷BI
(1)資料量級 <50列
介面耗時250ms 介面大小~100kb
渲染耗時 < 1s
(2)資料量級 ≥ 200列
介面耗時300ms 介面大小~300kb
渲染耗時 <3s
(3)資料量級 > 1W列
介面耗時2s 資料大小2M
渲染耗時~10s
網易有數表格元件較為簡單,只有簡單的資料展示和排序篩選,適用於明細資料展示場景。
Quick BI表格和敏捷BI在互動,視覺化能力,業務場景上都保持著同樣的功能,底層實現 Quick BI採用原生DIV+Flex佈局模擬表格實現,在渲染上比表格會有渲染的優勢,這點是瀏覽器自身渲染機制決定,我們內部實現需要滿足極端資料量下資料展示,所以特定做了橫向的虛擬列表優化,這種場景看業務需求,否則表格會過於複雜,得不償失。
表格渲染效能基本與Quick BI效能相當,極端情況下,敏捷BI依舊可以正常渲染,這點優於Quick BI。
資料預處理部分不由前端處理,交給後端,和後端協調好返回的資料結構,直接返回;
表格擴充套件的功能與表格耦合嚴重,表格渲染不夠純淨;
開發一個Headless UI,不依賴渲染框架,提供一個資料適配層,同時支援在Vue3生態上使用。
參考資料:
ali-react-table 站在巨人肩上,可惜不維護了