一个项目下来发现有不少看似类似但又有区别的功能,想一想若是一个一个的去写这种类似又有区别的功能,就会显的乏味枯燥,并且更关键的是这样的代码使得项目更难的去维护。如何让项目面目一新,而且方便之后需求迭代的时候更好的维护呢?vue
那就动手封装组件吧,让那些类似的功能需求都统一管理统一配置。git
当我拿到需求的时候,我先看了element-ui
的组件是否知足我业务上的需求(ps:若是在elemnt-ui组件的基础上去作改造也是ok的),但后来我发现element-ui的表格组件没法知足我此次的业务需求,经过改造element-ui
当中的组件也超级麻烦。最后,我仍是决定本身封装一个多功能的table组件吧.......github
功能需求以下:web
1.表格数据的树形渲染而且同个父级下的同层级可拖拽,不一样层级没法完成拖拽。npm
2.表格行可操做(ps:好比编辑,删除,查看详情)element-ui
3.表格的顶部可操做(ps:展开,排序)api
4.表格尾部可分页markdown
5.表格可多选(ps:表格带复选框)函数
npm install element-ui sortablejs -S
复制代码
在项目中我是使用sortablejs
实现拖拽的,以及使用elemnt-ui
的分页组件实现分页的功能。oop
由于是树形结构的数据,因此我想到了递归组件。在设计递归组件以前先了解树形结构的数据是长什么样的。
以下图经过给组件命名recursiveRow,而且在该组件的模板里使用该组件。好吧,到这里即完成了递归组件的第一步...
我将每一行设计成一个组件,若是该行数据有children那么就在渲染一次recursiveRow组件。好吧,到这里实现了递归的条件了。 接下来就是完成每一行数据的代码编写了。具体如何完成每一行数据的代码编写我等后续再补充,接下来咱们先来了接这个组件的配置表。
这个组件经过表头的配置实现了组件的统一管理,表头配置项的设计主要是经过一个JSON
来实现的。
JSON
是经过字段name
、props
、before
、images
、actions
、attr
、select
、tree
、name
、props
表格的基础配置是经过字段name
和props
来设计的。
这样的基础配置渲染出来页面以下:
通常一个表格组件不只仅只有渲染的功能就完事了,还会有其余的操做,好比删除,编辑,好比复选框,以及属性数据的样式配置等等...
行操做的表头配置我是经过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
以下图:
样式可自行修改哈~~
组件源码地址可查看:源码地址
为了知足掘有的需求,我又补充了一个选择父级以后全部的孩子都会被选中的需求。这里主要用到的思想就是数据更新视图以及事件流机制实现数据的获取。
tree-change
事件用来获取当前父选中以后的数据。以前的编辑有点小问题此次也一并修复了。
不少人可能会以为那么多组件库为何要本身封装组件呢,有这个必要吗?其实在分析这个需求的时候,我也曾尝试去找资源,但发现基本上都是文件夹同样的拖拽功能...因此,我后来就决定自行封装。
固然在开发的过程中,我以为封装组件是有必要的,既不会代码冗余显得臃肿并且还实现统一配置管理可让项目更稳步的实现迭代。
最后,对各大掘友有帮助的话但愿赏个 star ~(ps:不要吝啬哦)