在前端開發中,表格一直都是最複雜的組件之一。表格不只要支持豐富的操做(排序、過濾、搜索、分頁、自定義列等),還要有很是好的性能以展現大量數據。不少組件庫(例如 fusion design,ant design)提供了功能豐富的表格組件,這些表格一開始沒有過多考慮性能問題,每每是後面遇到性能瓶頸問題時才考慮添加虛擬滾動特性,但此時過多的表格功能使得性能優化的難度很是高。前端
ali-react-table (github.com/alibaba/ali…)是咱們小組開發的高性能 React 表格組件,咱們在一開始就考慮了表格的性能,爲其添加了內置的虛擬滾動特性。虛擬滾動會在數據量較大時自動開啓,輕鬆展現一萬行/一萬列以上的數據。虛擬滾動是表格的核心特性之一,在爲表格實現新功能時,咱們會確保新功能不與虛擬滾動衝突。react
表格組件的基本用法和 antd/fusion 表格相似,傳入 dataSource 來指定表格的數據源,傳入 columns 來對錶格的列進行配置。webpack
import { BaseTable } from 'ali-react-table'
function BasicUsage() {
const dataSource = [
{ 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' },
]
const columns = [
{ 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 <BaseTable dataSource={dataSource} columns={columns} />
}
複製代碼
當數據量較大時,表格會自動開啓虛擬滾動。git
↑ 經過 dataSource 傳入一個長度超過 5 萬的數組,表格依舊流暢。當表格向下滾動時,BaseTable 默認會爲表頭設置 style.position=sticky,表頭將會吸附在頁面或滾動容器的頂部。github
在維度、指標數量較多狀況下,設置 column.lock=true 能夠在表格左側或右側鎖定指定的列,提高交互體驗。web
在更復雜的狀況下,能夠設置 column.getSpanRect 來指定單元格的合併狀況;column.getSpanRect 返回每一個單元格被合併以後的矩形位置,在渲染表格時,BaseTable 會根據單元格的位置和對應的 spanRect,來爲單元格動態設置 rowSpan/colSpan,使得在虛擬滾動場景下合併單元格依然能夠生效。數組
基於這些實用的表格特性,咱們能夠在表格上進行深度定製與二次開發,實現下鑽、右鍵菜單、交叉表/透視表、收攏/展開等功能。同時表格內置的虛擬滾動保證了大數據量下表格仍具備很好的性能,上層使用者不須要擔憂性能問題。性能優化
下圖是基於 BaseTable 的一個簡單的透視表 demo(在線 demo):markdown
BaseTable 是一個相對底層的 React 組件,僅提供了基本的表格渲染功能。爲了方便對錶格進行功能拓展,咱們爲 BaseTable 設計了一個簡單的拓展方案,而後咱們基於該方案實現了一些常見的表格功能,包括排序、樹狀模式、列高亮等。antd
咱們知道 BaseTable
基於 dataSource 和 columns 來渲染表格,按照必定的規則對 dataSource/columns 進行包裝和轉換,能夠改變 dataSource/columns 的值或渲染輸出,實現特定的功能。
type Transform<T> = (input: T) => T
type TableTransform = Transform<{
columns: ArtTableColumn[]
dataSource: any[]
}>
複製代碼
TableTransform
(後面簡稱 transform)是一個純函數,輸入
,輸出一份新的
ali-react-table/biz 提供了一些常見表格功能的 transform,下面以「排序和列高亮兩個功能的組合」爲例介紹 transform 的使用方式。
對應的代碼以下:
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 = [
// 經過 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 使用起來更麻煩一些,但它的優點也很是明顯:
同時這也帶來了更清晰的表格功能設計分層:BaseTable 提供靈活的 column 配置來提供高可定製性,上層實現各種 transform 實現拓展功能。表格的基本功能由 ali-react-table
提供,而拓展功能則須要從 ali-react-table/biz
引入。 下表展現了 BaseTable 中列配置對象的結構,能夠看到上層能夠定製列標題、寬度、鎖列、單元格等內容,幾乎涵蓋了列的每一個方面。
ali-react-table/biz 還經過 commonTransform 提供了樹狀模式、自定義列、表格操做欄等功能,更多的功能也正在不斷開發中,將經過統一的拓展方式進行提供。
當遇到一些不常見的表格需求時,咱們能夠經過手動定製列的 render/getCellProps 來知足定製需求:
除了常見的行列數據,展現交叉數據或透視數據也是常見的表格需求。前述的 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) => {
// 可選的 自定義的渲染邏輯
return value
}}
/>
複製代碼
CrossTable 這裏就再也不過多介紹了,表格的效果可見本文上面透視表 demo 動圖。ali-react-table/pivot 還提供了一些透視數據處理方法,方便在前端進行一些簡單的數據聚合運算並將其展現到表格上,具體可見 相關文檔。
ali-react-table 的主要定位是提供高性能、高可定製性的 React 表格,方便上層進行封裝和定製並接入到不一樣的系統和業務中。ali-react-table 沒有綁定特定的 React 組件庫,僅依賴了一些工具類庫(例如 rxjs、styled-components、classnames),配合 webpack/rollup 的 tree shaking 特性,引入 ali-react-table 所產生的額外 JS 體積很是有限。
除了上面介紹的一些功能以外,ali-react-table 還提供了許多實用功能,包括表格操做欄、Excel 導出功能等(部分拓展功能須要安裝 fusion 組件庫)。組件已經在 GitHub 上開源,後續咱們也會不斷更新和維護 ali-react-table 的文檔,添加更多的代碼示例,歡迎你們使用~