根据公司的业务需求我是这样封装组件

12,103 阅读8分钟

前言

一个项目下来发现有很多看似相似但又有区别的功能,想想如果一个一个的去写这种相似又有区别的功能,就会显的乏味枯燥,而且更关键的是这样的代码使得项目更难的去维护。如何让项目焕然一新,并且方便以后需求迭代的时候更好的维护呢?

那就动手封装组件吧,让那些相似的功能需求都统一管理统一配置。

需求

当我拿到需求的时候,我先看了element-ui的组件是否满足我业务上的需求(ps:如果在elemnt-ui组件的基础上去做改造也是ok的),但后来我发现element-ui的表格组件无法满足我这次的业务需求,通过改造element-ui当中的组件也超级麻烦。最后,我还是决定自己封装一个多功能的table组件吧.......

功能需求如下:

1.表格数据的树形渲染并且同个父级下的同层级可拖拽,不同层级无法完成拖拽。

2.表格行可操作(ps:比如编辑,删除,查看详情)

3.表格的顶部可操作(ps:展开,排序)

4.表格尾部可分页

5.表格可多选(ps:表格带复选框)

多功能的表格组件

安装插件

npm install element-ui sortablejs -S

在项目中我是使用sortablejs实现拖拽的,以及使用elemnt-ui的分页组件实现分页的功能。

树形结构数据如何渲染

因为是树形结构的数据,所以我想到了递归组件。在设计递归组件之前先了解树形结构的数据是长什么样的。

如下图通过给组件命名recursiveRow,并且在该组件的模板里使用该组件。好吧,到这里即完成了递归组件的第一步...

我将每一行设计成一个组件,如果该行数据有children那么就在渲染一次recursiveRow组件。好吧,到这里实现了递归的条件了。 接下来就是完成每一行数据的代码编写了。具体如何完成每一行数据的代码编写我等后续再补充,接下来我们先来了接这个组件的配置表。

表头的配置项设计

这个组件通过表头的配置实现了组件的统一管理,表头配置项的设计主要是通过一个JSON来实现的。

JSON是通过字段namepropsbeforeimagesactionsattrselecttreenameprops

基础配置

表格的基础配置是通过字段nameprops来设计的。

这样的基础配置渲染出来页面如下:

其他配置

一般一个表格组件不仅仅只有渲染的功能就完事了,还会有其他的操作,比如删除,编辑,比如复选框,以及属性数据的样式配置等等...

行操作的表头配置我是通过actions配置进行的,可以传递点击的事件,并且执行该事件可以获取每行的数据,以及改行的索引,还有事件对象。其属性是通过attr来配置的。

如果需要复选框可通过配置select,将改字段设置为true。通过配置attr来配置属性,当然如果不传也可以,有默认值。那如何获取到每行勾选所对应的值呢?留个疑问,后续我们再讲述。

如果确定了哪个字段是需要渲染成树形的图案,可以通过配置tree,将改字段设置为true就可以,可以通过配置before可以特殊渲染每一个格子的数据。

当完成表头配置项的设计之后,如何传递属性,如何设计上面讲到的每行编码就是接下来要考虑的。

这里的核心是通过 v-bind,当v-bind不带参数的是将会把整个对象的所有属性都绑定到当前元素上。以及v-on将事件一一绑定到元素上。 组件中使用了 $attrs对象属性的集合和

$listeners对象事件的集合来实现属性的跨阶级传递,监听事件的传递。将绑定在table组件的属性和事件通过跨阶级传递给递归组件,使得递归组件接收属性和事件。

自定义递归组件:

有了表头和表格数据就可以实现每行的编码了,就是遍历table数据和表头数据.,来完成每行的编码,并将其属性进行绑定。在实现每行的过程中,使用了vue提供的一个动态组件component来实现动态的标签渲染。

功能集合

功能解析说明

  • 在实现业务需求功能的过程当中,因为我设计的是递归组件,所以每一个递归组件都是一个作用域。大部分人都会在递归当中混淆,它们只不过是层层嵌套的作用域,但它们又是独立的个体。(ps:在这里我也踩过坑)

  • 刚刚上面提到的如何获取每次勾选的值。是通过给table绑定select-change事件,该事件会返回每次所勾选的值。其实现的思想就是保存每次勾选的值,过滤每次反选的值,具体的想了解实现过程可查看源码。

  • 讲到表格的顶部,那我就把尾部一起讲了吧。在布局上顶部和尾部是通过具名插槽slot来实现的。所以可自定义顶部的操作以及尾部的分页。

  • 表格可展开是通过表格内部暴露出来一个函数openAllTree来实现的,可以通过绑定ref再外部获取这个函数。openAllTree其实现的思想是通过改变数据,让数据去驱动视图;在递归组件中封装一个函数用来将当前作用域的内部属性更新,在 table 组件中循环执行每一个递归组件的函数。具体的想了解实现过程可查看源码。

  • 表格可排序是通过绑定属性isSort来开启和关闭排序功能。给每一个递归组件绑定一个ref属性,那么通过new Sortable实例实现同层级的拖拽(这里的同层级就是相同层级节点并且同个父节点的可互拖)。其实现的思想是通过绑定ref属性可获取当前组件的数据流(ps:因为每一个组件都有自己的作用域,所以是独立的),那么通过数据再去驱动视图。在这里还要注意isSort的数据更改以及拖拽完成之后的表格数据更新,所以在通过接收属性callback来实现表格数据的更新(ps:回调函数的思想)。内部是通过函数handlerSort实现的,具体的想了解可查看源码。

  • 表格当前行删除操作是通过table组件内部暴露的函数handlerDepDel实现的。这里我考虑到数据是单向流动所以我通过传参的方式实现树形结构的数据删除。(ps:参数是最原始的数据列表和当前行的id)我个人觉得在当前行数据删除之后,只需调用删除的接口也就没必要再去调用获取总的数据列表的接口。等下次再进入页面,重新调用总列表的数据接口即可。

  • 新增了一个open属性,该属性代表是否一进入改页面就打开树形数据,该属性默认是false即不打开。

  • 最后在完成公司的业务需求之后,我又自己写了一个当点击编辑之后可直接在表格上修改数据的功能。主要是table组件暴露出的一个内部函数handlerEdit。其思路是通过增加改行的字段component来配置修改HTML标签,以及修改行的该字段的数据。具体的实现可查看源码。

  • 优化了表格的loading

使用table

自定义组件table

如下图:

样式可自行修改哈~~

源代码

组件源码地址可查看:源码地址

后记

为了满足掘有的需求,我又补充了一个选择父级之后所有的孩子都会被选中的需求。这里主要用到的思想就是数据更新视图以及事件流机制实现数据的获取。

  • 通过给table组件绑定tree-change事件用来获取当前父选中之后的数据。

说明

之前的编辑有点小问题这次也一并修复了。

总结

很多人可能会觉得那么多组件库为什么要自己封装组件呢,有这个必要吗?其实在分析这个需求的时候,我也曾尝试去找资源,但发现基本上都是文件夹一样的拖拽功能...所以,我后来就决定自行封装。

当然在开发的过程当中,我觉得封装组件是有必要的,既不会代码冗余显得臃肿而且还实现统一配置管理可以让项目更稳步的实现迭代。

最后,对各大掘友有帮助的话希望赏个 star ~(ps:不要吝啬哦)