ali-react-table:高性能React 表格組件

blank

ali-react-table:高性能React 表格組件

簡介

在前端開發中,表格一直都是最複雜的組件之一。表格不僅要支持豐富的操作(排序、過濾、搜索、分頁、自定義列等),還要有非常好的性能以展示大量數據。很多組件庫(例如fusion design,ant design)提供了功能豐富的表格組件,但這些表格一開始很少考慮性能問題,往往是後面遇到性能瓶頸問題時才考慮添加虛擬滾動特性,但此時過多的表格功能使得性能優化的難度非常高。

ali-react-table ( github.com/alibaba/ali- )是我們小組開發的高性能React表格組件,我們在一開始就考慮了表格的性能,為其添加了內置的虛擬滾動特性。虛擬滾動會在數據量較大時自動開啟,輕鬆展示一萬行/一萬列以上的數據。虛擬滾動是表格的核心特性之一,在為表格實現新功能時,我們會確保新功能不與虛擬滾動衝突。

表格組件的基本用法和antd/fusion 表格類似,傳入dataSource 來指定表格的數據源,傳入columns 來對錶格的列進行配置。 ( 在線範例

import{BaseTable}from'ali-react-table'functionBasicUsage(){constdataSource=[{prov:'湖北省',confirm:54406,cure:4793,dead:1457,t:'2020-02-15 19:52:02'},{prov:'廣東省',confirm:1294,cure:409,dead:2,t:'2020-02-15 19:52:02'},{prov:'河南省',confirm:1212,cure:390,dead:13,t:'2020-02-15 19:52:02'},{prov:'浙江省',confirm:1162,cure:428,dead:0,t:'2020-02-15 19:52:02'},{prov:'湖南省',confirm:1001,cure:417,dead:2,t:'2020-02-15 19:52:02'},]constcolumns=[{code:'prov',name:'省份',width:150},{code:'confirm',name:'確診',width:100,align:'right'},{code:'cure',name:'治愈',width:100,align:'right'},{code:'dead',name:'死亡',width:100,align:'right'},{code:'t',name:'更新時間',width:180},]return<BaseTabledataSource={dataSource}columns={columns}/>}
blank

虛擬滾動

當數據量較大時,表格會自動開啟虛擬滾動。 ( 在線範例

blank
通過dataSource 傳入一個長度超過5萬的數組,表格依舊流暢。當表格向下滾動時,BaseTable 默認會為表頭設置style.position=sticky,表頭將會吸附在頁面或滾動容器的頂部。

鎖列與單元格合併

在維度、指標數量較多情況下,設置column.lock=true 可以在表格左側或右側鎖定指定的列,提升交互體驗。

在更複雜的情況下,可以設置column.getSpanRect 來指定單元格的合併情況;column.getSpanRect 返回每個單元格被合併之後的矩形位置,在渲染表格時,BaseTable 會根據單元格的位置和對應的spanRect,來為單元格動態設置rowSpan/colSpan,使得在虛擬滾動場景下合併單元格依然可以生效。

基於這些實用的表格特性,我們可以在表格上進行深度定制與二次開發,實現下鑽、右鍵菜單、交叉表/透視表、收攏/展開等功能。同時表格內置的虛擬滾動保證了大數據量下表格仍具有很好的性能,上層使用者不需要擔心性能問題。

下圖是基於BaseTable的一個簡單的透視表範例( 在線範例):

blank

(囧rz動圖太大,知乎上只能展示一小部分,並且壓縮後質量感人, 完整動圖鏈接

表格功能拓展

BaseTable 是一個相對底層的React 組件,僅提供了基本的表格渲染功能。為了方便對錶格進行功能拓展,我們為BaseTable 設計了一個簡單的拓展方案,然後我們基於該方案實現了一些常見的表格功能,包括排序、樹狀模式、列高亮等。

我們知道BaseTable基於dataSource和columns來渲染表格,按照一定的規則對dataSource/columns進行包裝和轉換,可以改變dataSource/columns的值或渲染輸出,實現特定的功能。

type Transform<T> = (input: T) => T type TableTransform = Transform<{ columns: ArtTableColumn[] dataSource: any[] }>

TableTransform (後面簡稱transform)是一個純函數,輸入列配置+數據源,輸出一份新的列配置+數據源。每一個transform 通過對dataSource/columns 的包裝和轉換以實現一個新的特性。注意到每個transform 的輸入和輸出的類型相同,我們可以將多個transform 串聯以實現不同功能的組合。

ali-react-table/biz提供了一些常見表格功能的transform,下面以「排序列高亮兩個功能的組合」為例介紹transform的使用方式。

blank

對應的代碼如下:

import { ArtColumn, BaseTable } from 'ali-react-table' import { applyTransforms, commonTransforms } from 'ali-react-table/biz' import React, { useState } from 'react' function SingleSortExample() { const { isLoading, dataSource } = useProvinceDataSource() const columns: ArtTableColumn[] = [ // 通过features.sortable 来标记可排序的列{ code: 'provinceName', name: '省份', features: { sortable: true } }, { code: 'confirmedCount', name: '确诊', features: { sortable: true } }, { code: 'curedCount', name: '治愈', features: { sortable: true } }, { code: 'deadCount', name: '死亡', features: { sortable: true } }, { code: 'updateTime', name: '最后更新时间' }, ] // transform 都是纯函数,所需的额外状态需要上层提供,这里使用useState 来快速创建状态const [hoverColIndex, onChangeHoverColIndex] = useState(-1) const [sorts, onChangeSorts] = useState([ { code: 'deadCount', order: 'desc' }, ]) const renderData = applyTransforms( { columns, dataSource }, commonTransforms.columnHover({ hoverColIndex, onChangeHoverColIndex }), // 设置sort.mode=multiple 可以使用多列排序commonTransforms.sort({ mode: 'single', sorts, onChangeSorts }), ) // applyTransform 是使用多个transform 的辅助函数// 上面的代码相当于: // input = { dataSource, columns } // t1 = commonTransforms.columnHover(...) // t2 = commonTransforms.sort(...) // renderData = t2(t1(input)) return ( <BaseTable dataSource={renderData.dataSource} columns={renderData.columns} /> ) }

相比於原來的「直接通過props 設置表格配置」的方式,transform 使用起來更麻煩一些,但它的優勢也非常明顯:

  • 對於用戶:
    • 按需引入transform,多個transform 可組合性較好,降低多個功能之間的衝突概率
      • 因為原來表格封裝了所有功能,多個功能之間非常容易發生衝突
    • 表格功能不滿足業務需求時,可自行實現自定義transform,與commonTransforms 配合使用
  • 對於表格組件維護者(也就是我):拓展功能所需的狀態由上層提供,表格內部的狀態數量可控,降低表格性能優化的難度,表格組件維護起來比較容易

同時這也帶來了更清晰的表格功能設計分層: BaseTable提供靈活的column配置來提供高可定制性,上層實現各類transform實現拓展功能。表格的基本功能由ali-react-table提供,而拓展功能則需要從ali-react-table/biz引入下表展示了BaseTable 中列配置對象的結構,可以看到上層可以定制列標題、寬度、鎖列、單元格等內容,幾乎涵蓋了列的每個方面。

blank

ali-react-table/biz 還通過commonTransform 提供了樹狀模式、自定義列、表格操作欄等功能,更多的功能也正在不斷開發中,將通過統一的拓展方式進行提供。

blank
左:樹狀模式;右:自定義列

當遇到一些不常見的表格需求時,我們可以通過手動定制列的render/getCellProps 來滿足定制需求:

blank

交叉表

除了常見的行列數據,展示交叉數據或透視數據也是常見的表格需求。前述的BaseTable 只能夠展示行列異構的數據:行(dataSource)負責提供數據,列(columns)控製表格如何展現;而交叉/透視數據的行表頭和列表頭是同構的(行表頭和列表頭都是樹狀結構)。為了方便展示行列同構數據,我們基於BaseTable 實現了一個簡單的交叉表格(CrossTable),專門應對「行表頭和列表頭都是一棵樹」 的場景。

ali-react-table/pivot 提供的交叉表(CrossTable)也是一個較為底層的React 組件,僅提供表格結構的渲染能力。 CrossTable的渲染過程可認為是:左树+ 上树=> 表格。大致使用方式如下:

<CrossTable// 推荐为交叉表设置一个默认列宽
defaultColumnWidth={100}// leftTree, topTree 均为 { key, value, children } 的嵌套树状结构
leftTree={leftTree}topTree={topTree}getValue={(leftNode,topNode)=>{// 自定义的取数逻辑,针对每个单元格都会调用一次
// leftNode 表示当前单元格对应的左侧树节点,topNode 是对应的上方树节点
}}render={(value,leftNode,topNode)=>{// 可选的 自定义的渲染逻辑
returnvalue}}/>

CrossTable 這裡就不再過多介紹了,表格的效果可見本文上面透視表demo 動圖。 ali-react-table/pivot還提供了一些透視數據處理方法,方便在前端進行一些簡單的數據聚合運算並將其展示到表格上,具體可見相關文檔

小結

ali-react-table 沒有綁定特定的React 組件庫,僅依賴了一些工具類庫(例如rxjs、styled-components、classnames),配合webpack/rollup 的tree shaking 特性,引入ali-react-table 所產生的額外JS 體積非常有限。 ali-react-table 的主要定位不是提供開箱即用的業務表格,而是提供高性能、高可定制性的React 表格,方便上層進行封裝和定制並接入到不同的系統和業務中。

除了上面介紹的一些功能之外,ali-react-table 還提供了許多實用功能,包括表格操作欄、Excel 導出功能等(部分拓展功能需要安裝fusion 組件庫)。組件已經在GitHub上開源,後續我們也會不斷更新和維護ali-react-table的文檔,添加更多的代碼範例,歡迎大家使用~

What do you think?

Written by marketer

blank

nodejs事件循環階段之定時器

blank

目標是最完善的微前端解決方案- qiankun 2.0