HomeAuthorContactSearch

9102年了,前端怎么做数据处理?

Query It  Pure Javascript  data processing tool. Including filter, sort, search, paginate. Support browser & Node.js.

写前端经常会遇到做数据展示的场景。一般情况下,数据的处理由后端处理,前端只需要传递相应参数。不过因为前端框架已经趋于成熟,少量的数据处理倾向在前端处理。那么,是否有通用的数据处理逻辑,这个逻辑能不能抽象称为一个可复用的工具?

什么是数据处理

首先我们要明确,数据处理是什么。一般情况下,数据的处理可以被划分为四类:筛选,搜索,排序,分页。

筛选

常见的筛选逻辑为:**当某条记录一个或多个字段同时满足某些条件中的一个时,该条记录有效。**说起来很拗口,我们举一个例子来说明,这里有一个表格,表格的每行代表一条记录,每列代表一个字段,接下来我们要进行一些筛选:

编号 姓名 年龄
1 张三 18
2 李四 18
3 王五 19

每个筛选条件都可以被抽象为一个对象,用来表示筛选条件:

编号为1的记录

{
	id:1
}

年龄为18且姓名为张三的记录

{
  age:18,
  name:"张三"
}

年龄为18或19的记录

{
  age:[18,19]
}

年龄大于18的记录

{
  age: (val) => val > 18
}

这几种组合几乎涵盖了所有简单的筛选条件,满足绝大多数场景的需求。或者想做更复杂的搜索,可以考虑使用sift.js这样的库。

搜索

前端搜索也是很常见的需求,一般情况下可以划分为:单字段搜索和多字段搜索。即某条记录的某一字段或多个字段是否同时满足搜索条件,fuse.js 提供了非常好用的模糊搜索功能。

排序

排序可以划分为单项排序和多项排序,排序项有先后顺序。当第一个排序项出现相同的情况,会根据第二个排序项将未排序的记录进行排序,以此类推。lodash.orderBy 提供了很方便的排序功能。

分页

分页是很简单的逻辑,只需要知道页面容量当前页就可以算出要显示的内容:

function paginateData(data: any[], current: number, size?: number) {
  if (size === undefined || size < 0) {
    return [...data];
  }
  if (current < 1) {
    current = 1;
  }
  return data.slice((current - 1) * size, current * size);
}

设计API

怎么设计API因人而异,没有最好用的只有最适合的。我个人比较喜欢使用类的语法。 详情请查看https://github.com/myWsq/query-it/blob/master/src/query-it.ts

export class List<T> {
  private _source: T[] = [];
  
  /**
   * 展示数据
   */
  public items: T[] = [];

  load() {}
  search() {}
  filter() {}
  sort() {}
  setPageSize() {}
  setCurrentPage() {}
}

接下来就可以开始写这些方法了。不难发现,最终的数据是经过这几个方法层层处理的,所以方法的调用存在一定的先后顺序,而且根据用户的使用习惯,这些方法之间也会有一些相互的影响。




从图中可以看出我们一共将数据处理分为了三级,按照使用习惯,在进行了上一级的操作以后,我们一般会清除分页条件。即在筛选,搜索,排序后,将页数置为1.
我们还发现,下一级的处理条件改变并不会影响上一级的处理结果。为了提高处理的效率,我们将缓存每一级的处理结果。
Untitled Document (4).jpg

配合框架使用

做完了抽象和总结,下面就需要考虑怎么配合框架,实现逻辑复用,不然总结的再好也没什么意思。实现逻辑复用,必然需要封装。封装本身很简单,麻烦的点在于封装到什么程度,需要在灵活性和便捷性上做取舍和平衡。

组件化

即提供一个组件用于展示数据,配套相应的控件用于数据处理。这样做的好处是非常方便,只需要将数据作为参数传入组件,即可实现所有的常规处理逻辑。代表性的例子有 Vuetify.js - Data Table, Antd - Table。这些组件提供的数据处理功能基本上可以满足大多数的需求。

有一说一,确实方便。不过在做项目的时候就会发现,并不是所有的数据都能用表格来展示。有时候是列表,有时候是卡片,展示的方式多种多样。面对这些需求时,又需要重新写一套处理逻辑,费时又费力。

Extend / Mixin

React 或 Angular 称为继承(extend), Vue一般叫混入(mixin)。各大框架都提供了这种比较高阶的逻辑复用方式。使用这种方法可以在纯逻辑层面实现复用而不会影响视图层。在hooks api未被提出前,这是纯逻辑复用的首选方式。

在实际的项目中,纯逻辑复用表现地非常不错。虽然需要写一些重复冗余的样式和一些模板代码,但它足够灵活,可以实现绝大多数需求。那一小撮实现不了的是非常复杂的数据处理,一般是复合排序或者组合筛选之类的。这部分确实单独处理比较好。

缺点也比较明显,首当其冲的就是命名冲突。继承或混入后新的变量或函数会覆盖原来的,导致一些功能出现问题,这种BUG还特别难找。

Hooks API / Composition API

React提出的Hooks API为前端带来的改变是革命性的,稳稳地扎在了纯逻辑复用的痛点上。了解并使用后就会发现,这种编程方式彻底解决了命名冲突的问题。

使用类Hooks的方式实现数据处理逻辑复用是目前的最佳方法,这让我们找到了效率和灵活之间微妙的平衡点。这里给出了示例用法

为何要在前端进行数据处理

因为简单。

大大减小因为制定接口规范带来的沟通成本,降低因为接口block前端开发的风险。提高开发效率,减少冗余接口,让项目能更好地快速迭代。
缺点也很明显,不能处理复杂逻辑,大批量数据和动态数据。

当满足以下条件时,在前端做数据处理是比较好的选择:

  1. 处理逻辑简单,只需要字符层面的操作。如:比较是否相等,比较大小等;
  2. 数据量不大时。这里的数据量指数目和大小,数目在十万级以下的数据比较适合。结合网络环境判断,public环境下一般单次请求的数据量不超过1MB。
  3. 数据相对稳定。前端的数据是在首次请求时定死的,数据不会实时变化,如果对数据的实性要求比较高,则不适合在前端做处理。