专栏名称: 尤水就下
前端开发
目录
相关文章推荐
彬彬有理  ·  《哪吒2》爆火的原因,很残忍 ·  昨天  
中国基金报  ·  刚刚!5000亿央企,重组大动作! ·  3 天前  
中国基金报  ·  突发!俄罗斯遭袭 ·  3 天前  
51好读  ›  专栏  ›  尤水就下

table 组件了解两下?

尤水就下  · 掘金  ·  · 2019-12-04 07:25

正文

阅读 1200

table 组件了解两下?

前言

午后写的这篇文章,微风加阳光,很是惬意🏖。
然后,话不多说,我们直接进入正题,今天主要会讲解一下 可伸缩列、固定列、多级表头 和几个表格的常见问题✍️,干货满满哦😯。

温馨提示:本篇文章是继上次 table 组件了解一下? 这篇文章之后写的,所以建议先去阅读一下前面那篇文章👀。当然也可以直接往下看,因为这里主要说下思路,没放多少代码😁。

可伸缩列

可伸缩列,顾名思义就是我们可以通过拖动表头的 border 来实现列宽的大小,看看下面这张图你就懂了👇:

上图的意思应该表达的挺明了,现在我们简要看下具体实现步骤👇:

第一步:

我们在表头的每个 th 里面都多增加一个 div ,也就是上图中红色的部分,然后绝对定位于 th 的右边即可。

第二步:

在表头 header 和表体 body 同级的地方增加一个 div 来表示上图中的虚线,默认是隐藏的,就像下面这张图:

第三步:

对红色部分的鼠标事件进行监听:当鼠标按下的时候,就显示虚线,并实时改变虚线的 left 值,当鼠标抬起的时候就隐藏虚线,并计算出拖拽后的列宽,之后修改的 columns 里面对应列的 width 即可,这样表头和表体的列宽都会同步改变。 当然,还要记得在鼠标抬起时解绑 mousemove mouseup 事件,这是个好习惯。
以上就是可伸缩列的实现方式。

固定列

接下来我们来看看固定列是怎么实现的。首先还是 api 的设计,这个应该容易想到,我们在 columns 里面把需要固定的列添加上 fixed 属性即可,它的值有两种选择( left right ),就像下面这样:

columns: [
    {
      title: '姓名',
      key: 'name',
      width: 100,
      fixed: 'left'
    }
    ...
]
复制代码

为了简化问题,这里我们只考虑左列固定,因为右列固定也是一样的。
这里先一句话说明下原理😁:就是多渲染一份表格并绝对定位在原来的表格之上,下面这张图应该能帮助你理解👇:

其实上图已经是实现的核心了,So,我们就直接说下具体实现方式😎:

第一步:处理表格数据

因为要把需要固定的列放到左边,所以我们一开始最需要处理的就是表格数据,把所有有 fixed="left" 属性的列排在前面。

第二步:渲染两个表格

再来就是正常渲染两个表格(A 和 B),事实上表格 A 和表格 B 是一模一样的,只不过表格 B多设置了一些属性,比如绝对定位在左上角、定宽(宽度就是有 fixed="left" 属性的列的宽度之和)、溢出隐藏啊等。具体代码结构如下图所示:

此外,对于表格 A 的 fixed 部分我们可以设置 visibility: hidden ,因为它不需要展示,而且 Element 也是这样写的;同样地,对于表格 B 的非 fixed 部分也可以设置 visibility: hidden
这时候我突然产生了一个疑问🤔,就是 为什么要设置成 visibility: hidden 而不设置成 display: none 呢? display: none 难道不是可以渲染更少的 dom 吗?设置成 visibility 的意义在哪里? 这是个不错的问题,建议大家思考一下下。。。再往下看😁。

带着疑问我顺便看了下 iView 和 Ant Design 的 dom 结构,发现 iView 也是用 visibility: hidden 来处理的,而 Ant Design 则是直接不渲染了,这就很奇怪啦!于是乎我把 iView 和 Element 的 visibility: hidden 换成 display: none 试了一下,发现好像也 ok,表格展示也是对的,没什么问题,所以是为什么呢?其实最主要的原因是把 visibility: hidden 换成 display: none 会引发 行对不齐 的问题。
什么意思呢?就是说如果我们设置 display: none 的话,表格 A 里面的行高是不固定的,但这时候表格 B 是没有展示表格 A 中表体的内容,所以表格 B 不能同步表格 A 里面的高;而如果我们设置成 visibility: hidden 的话,表格 A 和表格 B 其实是都包含所有数据的,只是视觉上不可见而已,这样它们的行高就能够保持完全一致,虽然会导致多余的 dom 元素。
那 Ant Design 为什么可以呢?其实 Ant Design 也是有这个问题的,虽然它没有渲染多余的 dom,但是它会事先计算出表格 A 的行高,然后在去同步设置固定列的行高,此外在表格大小、列宽变化的时候也要去同步。它们是两种不同的方案,大家自己好好体会一下🙌。
当然,这里我们采用的是 Element 和 iView 的方案。

第三步:同步滚动

上面的实现方式会有什么问题呢🤔? 最明显的问题就是表格 A 和表格 B 是割裂的, 所以滚动其中一个表格的时候,另外一个表格是不会跟着响应的。事实上每个表格里面的表头和表体也是割裂的,所以现在我们要做就是同步滚动:当我们横向滚动表格 A 的表体时,我们需要同步滚动表格 A 的表头部分;当我们纵向滚动表格 A 的表体时,我们需要同步滚动表格 B 的表体部分。
这里以表格 A 里面的表体( A__body )滚动为例子,简要说下具体做法: A__body 横向滚动时:获取 A__body scrollLeft 值,然后把值同步到表格 A 的表头; A__body 纵向滚动时:获取 A__body scrollTop 值,然后把值同步到表格 B 的表体。 当然滚动是一个高频动作,所以我们可以进行防抖处理;还可以尝试用 transform 来替代 scrollTop scrollLeft 进行滚动。

第四步:同步hover

同第三步一样,当我们 hover 每一行的时候也需要像滚动一样进行同步,也就是 hover 样式的同步。
同样地我们以 hover 表格 A 的表体为例🌰,监听行的 mouseenter mouseleave 事件,当鼠标移入到某一行,这时候你是能获取该行信息的,然后同步到固定列的对应行即可;同理 hover 到固定列的时候也要同步到表格 A,这里就不再赘述。

多级表头

表头

首先还是 api 的设计,我们希望在 columns 里面加个 children 就能实现,就像下面这样(顺便看下多级表头长什么样):

columns: [
    {
      title: '日期',
      key: 'date',
      width: 200
    },
    {
      title: '配送信息',
      children: [
        {
          title: '姓名',
          key: 'name',
          width: 200
        },
        {
          title: '地址',
          key: 'addr'
        }
      ]
    }
]
复制代码

从上图中我们可以看出多级表头其实是一行一行来渲染的,每行又可以分为一列一列来渲染,实际上就是一个二维数组,两个 v-for 循环,让我们截个 iView 的代码看看大体结构:
所以我们首先要做的就是对 columns 进行格式化,使其变成 下面这个样子(二维数组):
其中每个数据节点都会给个 level rowspan colspan ,后面两个是用来实现合并单元格的,比如 columns 的第一项日期,它的 colspan 应该是它 children 的总数,如果没有 children 就是1;它的 rowspan 应该等于最大层数-当前层数+1,如果有 children 则为1。
这里可能会有点绕,所以需要停下来理一理思路🤔,但其实本质就是数据结构的转换,所以这里没有特别强调说明如何转换,大家自己动手试一试吧😊。

表体

表体渲染就简单了,只不过我们同样要对 columns 进行一下处理,我们先看看整理之后的列大概长什么样:

其实新的 columns 就是遍历一下旧的 columns ,有 children 就继续遍历获取子列,没 children 就直接取出该列,类似于获取到所有叶子节点的列,这个新的列会代替原有的 columns 来渲染,由于这个数据结构转换简单点,所以我把代码贴了上来:







请到「今天看啥」查看全文