乐趣区

关于前端:从复杂到简单BaseTablePresenter让前端表格操作更加轻松

明天讲一个治理后盾罕用的性能,表格性能

需要剖析

首先看看上面的表格页面,有筛选,分页,渲染等性能

依据这些性能咱们能够定义出页面视图须要的数据模型

视图数据模型

首先是分页相干的数据

interface Pagination {
  current: number;
  total: number;
  pageSize: number;
}
  • data: 每一行的数据
  • params: 申请参数
  • loading: …

    export interface TableState<Row = any> {
    loading: boolean;
    data: Row[];
    params: Record<any, any>;
    pagination: Pagination & Record<string, any>;
    [p: string]: any;
    }

    有了这些数据就能够满足一个表格的失常渲染了

    视图操作方法

    接下来定义一些表格须要的操作方法

    loading 管制

    这里咱们用两个办法来管制 loading 的切换

    showLoading() {if (this._loadingCount === 0) {this.setState((s) => {s.loading = true;});
      }
      this._loadingCount += 1;
    }
    
    hideLoading() {
      this._loadingCount -= 1;
      if (this._loadingCount === 0) {this.setState((s) => {s.loading = false;});
      }
    }

    fetchData

    获取数据的办法,这里咱们定义一个异步函数,申明好接口定义,函数内容间接抛错,须要等具体应用在去实现真正的 fetchData

     async fetchData(params: Partial<TableState['params']> & {
        current: number;
        pageSize: number;
      },
    ): Promise<{data: any[];
      current: number;
      pageSize: number;
      total: number;
    }> {throw Error('请实现 fetchTable');
    }

    updateData

    updateData 办法用来调用 fetchData,更新数据,切换 loading

    /**
     * 发申请,更新数据
     * @returns
     */
    updateData() {const params: Record<any, any> = {};
      Object.entries(this.state.params || {}).forEach(([k, v]) => {if (v !== undefined) {Object.assign(params, { [k]: v });
        }
      });
      this.showLoading();
    
      return this.fetchData({
        current: this.state.pagination.current || 1,
        pageSize: this.state.pagination.pageSize || 10,
        ...params,
      })
        .then((res) => {this.setState((s) => {
            s.pagination.current = res.current;
            s.pagination.pageSize = res.pageSize;
            s.pagination.total = res.total;
            s.data = res.data;
          });
          return res;
        })
        .finally(() => {this.hideLoading();
        });
    }

    setPagination

    setPagination,顾名思义就是更新分页状态的办法,这里有一个潜在的逻辑,当 pageSize 批改的时候,分页要切换为第一页

    /**
     * 更新参数,如果批改的参数是不是 current,重置 current 为 1
     * @param pagination
     */
    setPagination(pagination: Partial<Pagination>) {this.setState((s) => {
        let current = pagination.current || s.pagination.current;
    
        if (pagination?.pageSize) {if (pagination.pageSize !== s.pagination.pageSize) {current = 1;}
        }
    
        s.pagination = {
          ...s.pagination,
          ...pagination,
          current,
        };
      });
    }

参数设置

上面是更改参数和重置参数的办法

  /**
   * 更新参数,重置以后申请页面为 1
   * @param params
   */
  setParams(params: Partial<TableState['params']>) {const d: Partial<TableState['params']> = {};
    Object.entries(params).forEach(([k, v]) => {if (v !== undefined) {
        Object.assign(d, {[k]: v,
        });
      }
    });

    this.setState((s) => {
      s.params = {
        ...s.params,
        ...d,
      };

      s.pagination.current = 1;
    });
  }

  resetParams() {this.setState((s) => {s.params = {} as Record<any, any>;
    });
  }

分页状态批改

很多时候咱们都是用回调函数去监听 ui 组件的分页状态批改,这里也提供一个对应的办法

  /**
   * 切换页面,并且更新数据
   * @param p
   */
  onPageChange(p: Pagination) {this.setPagination(p);

    return this.updateData();}

残缺的代码例子

残缺的代码例子能够查看 github 仓库,记得给个 star 哈

如何应用

npm install @clean-js/pro-presenters
import React from 'react';
import {usePresenter} from '@clean-js/react-presenter';
import {BaseTablePresenter} from '@clean-js/pro-presenters';

export const demo = () => {const { presenter, state} = usePresenter(BaseTablePresenter);
  return (
    <Table
      loading={state.loading}
      columns={state.columns}
      pagination={state.pagination}
      dataSource={state.data}
      onChange={(p) => {presenter.onPageChange?.(p);
      }}
    />
  );
};

当咱们有多个表格要应用的时候,初始化多个 presenter 即可
上面的两个 usePresenter 会返回不同的实例

import React from 'react';
import {usePresenter} from '@clean-js/react-presenter';
import {BaseTablePresenter} from '@clean-js/pro-presenters';

export const demo = () => {const { presenter, state} = usePresenter(BaseTablePresenter);
    const {p: tableP} = usePresenter(BaseTablePresenter);
    return (
        <div>
             <Table
              loading={state.loading}
              columns={state.columns}
              pagination={state.pagination}
              dataSource={state.data}
              onChange={(p) => {presenter.onPageChange?.(p);
              }}
            />
             <Table
              loading={tableP.state.loading}
              columns={tableP.state.columns}
              pagination={tableP.state.pagination}
              dataSource={tableP.state.data}
              onChange={(p) => {tableP.onPageChange?.(p);
              }}
            />
        </div>
    );
};

如何拓展性能

假如咱们须要增加一个 columns 属性,用来配置 UI,咱们能够继承这个 BaseTablePresenter,对其进行扩大即可

type Columns = {key: string}[];

class CustomTableP extends BaseTablePresenter<
  {name: string},
  {columns: Columns}
> {constructor() {
    super({data: [],
      loading: false,
      params: {},
      pagination: {
        current: 1,
        pageSize: 10,
        total: 1,
      },
      columns: [],});
  }

  setColumns(columns: Columns) {this.setState((s) => {s.columns = columns;});
  }

  test() {return 'test';}
}
export const demo = () => {const { presenter, state} = usePresenter(CustomTableP);
  return (
    <Table
      loading={state.loading}
      columns={state.columns}
      pagination={state.pagination}
      dataSource={state.data}
      onChange={(p) => {presenter.onPageChange?.(p);
      }}
    />
  );
};

npm 包地址
github 地址,记得给个 star 哈

退出移动版