AG Grid
Ag-Grid是一个功能强大且高度可定制的数据网格组件,本身纯JS实现,且很方便与React、Angular和Vue等流行框架集成,具有以下特点:
- 功能性极强:默认提供了排序、过滤、分组、编辑等丰富的数据操作功能,不需要特别定制就可以满足各种复杂的数据处理需求。
- 高度可扩展性:支持无限滚动、行拖动等诸多特性,且可以根据业务需求进行灵活配置和扩展,自定义功能特别细致且设计得很好。
服务端渲染
虽然Ag-Grid本身是一个客户端JavaScript库,主要依赖于浏览器端的渲染能力,但它可以通过与服务端的配合,实现高效的数据加载和渲染。
- 数据预加载与分页:Ag-Grid可以与服务端进行数据交互,实现数据的预加载和分页功能。这意味着,当处理大量数据时,可以通过服务端渲染先加载部分数据,然后根据用户的滚动或分页请求,动态加载更多数据,从而提高页面加载速度和响应性能。
- 服务器端排序与过滤:结合服务端的处理能力,Ag-Grid可以实现服务器端排序和过滤功能。用户可以在客户端进行排序或过滤操作,但这些操作实际上是在服务端完成的,从而减少了客户端的计算负担,提高了处理效率。
- 自定义渲染器与服务端数据:Ag-Grid支持自定义渲染器功能,允许开发人员根据特定需求自定义单元格的渲染方式。在服务端渲染的场景下,自定义渲染器可以与服务端数据进行交互,根据服务端返回的数据动态调整单元格的显示内容和样式。
基本结构
数据网格简单说来就是对行数据的呈现,无论其呈现看上去多么复杂,其内部结构都是平铺的行数据,为了方便说明什么是平铺的行数据,说几个常见的场景:
- 基本数据呈现,就是一层结构,因为并没有特殊的呈现,可以看出数据就是一个行数组;
- 进行分组(Group),这时候数据看上去就有层级结构,但实际上,如果不去管Group的子层级,外层和基本数据呈现一样,都是一层结构,数据上也是一个行数组;
展开之后,子层级实际上是独立的部分,也是基本的数据呈现,因此还是一个行数组:
无论Group多少层,每一层都是独立的部分,只是UI组织上看上去有层级关系而已,之所以反复强调说数据没有层级,是为了后续的缓存和服务端数据获取等。
- 进行交叉(Pivot),这时候表头看上去就比较复杂,但按照上面的说法,数据依旧是平的。
暂且不管表头和行数据如何对应,因为这些只属于细节部分,就主体而言,数据还是平的,这点非常重要。
结论
综上所述,AG Grid的数据最基本的机构就是简单的行数据数组,可以将这样的行数据数组称之为数据块,有了简单的数据块,那后续的功能就比较好实现了。
- 排序、筛选等
因为无论呈现结构上多复杂,底层都是对简单数据块的展示,因此排序和筛选也都是对数据块的操作,数据块没有层级结构,因此这些可以认为是对单层数组的操作,所以比较清晰。
多层结构实际上是单层结构的嵌套,都是单独处理然后拼装起来,所以处理上和单层结构一致
- 获取数据
因为数据以单层数据块为单元,因此如果想要实现从服务端获取数据,那也是一次请求获取一个行数据数组,没有复杂的结构,因此服务端数据也比较清晰。
- 数据缓存
以数据块为基础的缓存系统也比较直接,就是设定一块数据有多少行,最多缓存多少个数据块就行,不需要考虑复杂的层级嵌套结构,也比较清晰。
具体实现
上述简单说明了AG Grid以简单数据块为基本的数据结构,这样的数据结构让看似复杂的网格操作在底层以简单平整的方式进行。接下来,就以一个简单的服务端数据模型的实现来更加细致地说明具体参数是如何与这样的数据块对应的,服务端以Node实现,用的Sqlite数据库。
根据上文所说,服务端数据服务的唯一目标就是根据请求的参数(也就是当前UI的状态)来查询出一层的数据块数据,并不需要在意什么嵌套之类的操作,因此,落到具体实现,就是根据参数拼接SQL,拼接的过程大概如下:
buildSql(req) {
this.builder = db.where(true)
this.createSelectSql(req)
this.createWhereSql(req)
this.createLimitSql(req)
this.createOrderBySql(req)
this.createGroupBySql(req)
console.log(this.builder.toSQL().toNative())
return this.builder
}
官方有Node版本基于MySQL的实现,见github.com/ag-grid/ag-… ,不过需要注意里边使用的AG Grid版本有些老,因此如果想要参考其client文件夹里的写法时,需要注意新版本AG Grid的datasource.getRows方法的参数有点修改。
fetch('./olympicWinners/', {
method: 'post',
body: JSON.stringify(params.request),
headers: {"Content-Type": "application/json; charset=utf-8"}
})
.then(httpResponse => httpResponse.json())
.then(response => {
// 新版本改成了params.success({rowData: response.rows,rowCount: xxx})
params.successCallback(response.rows, response.lastRow);
})
.catch(error => {
console.error(error);
// 新版本改成了params.fail()
params.failCallback();
})
不过既然标题写的是Knex,那这里就用Kenx来做个说明,比SQL要稍微直观些。
根据buildSql的执行顺序可以看出是根据select、where、limit、orderBy、group这些部分来一步步完善SQL的,各个步骤相对独立也不复杂。
待续……