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

簡介

在前端開發中,表格一直都是最複雜的組件之一。表格不只要支持豐富的操做(排序、過濾、搜索、分頁、自定義列等),還要有很是好的性能以展現大量數據。不少組件庫(例如 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} />
}
複製代碼
Copy

image.png

虛擬滾動

當數據量較大時,表格會自動開啓虛擬滾動。git

dvt-table-large-datasource.gif
↑ 經過 dataSource 傳入一個長度超過 5 萬的數組,表格依舊流暢。當表格向下滾動時,BaseTable 默認會爲表頭設置 style.position=sticky,表頭將會吸附在頁面或滾動容器的頂部。github

鎖列與單元格合併

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

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

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

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

dvt-table-large-virtual2.gif

表格功能拓展

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

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

type Transform<T> = (input: T) => T

type TableTransform = Transform<{
  columns: ArtTableColumn[]
  dataSource: any[]
}>
複製代碼
Copy

TableTransform(後面簡稱 transform)是一個純函數,輸入

**列配置 + 數據源**

,輸出一份新的

列配置 + 數據源
。每個 transform 經過對 dataSource/columns 的包裝和轉換以實現一個新的特性。注意到每一個 transform 的輸入和輸出的類型相同,咱們能夠將多個 transform 串聯以實現不一樣功能的組合。

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

對應的代碼以下:

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} />
}
複製代碼
Copy

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

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

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

image.png

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

image.png
↑ 左:樹狀模式;右:自定義列

當遇到一些不常見的表格需求時,咱們能夠經過手動定製列的 render/getCellProps 來知足定製需求:
image.png

交叉表

除了常見的行列數據,展現交叉數據或透視數據也是常見的表格需求。前述的 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
  }}
/>
複製代碼
Copy

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 的文檔,添加更多的代碼示例,歡迎你們使用~

[關注](https://www.atatech.org/articles/169085/follow)
0人關注該文章
[評論文章 (0)](https://www.atatech.org/articles/169085#comment) [15](https://www.atatech.org/articles/169085/voteup "贊")
0

1 收藏

相關文章
相關標籤/搜索