我是如何让公司后台管理系统面目一新的(下)-封装组件

写在前面

上篇在这里前端

立刻到了金三银四的时间,不少公司开启了今年第一轮招聘的热潮,虽然说今年是互联网的寒冬,可是只要对技术始终抱有热情以及有过硬的实力,即便是寒冬也不会阻挠你前进的步伐。在面试的时候,每每在二面,三面的时面试官会结合你的简历问一些关于你简历上项目的问题,而如下这个问题在不少时候都会被问到vue

在这个项目中你有遇到什么技术难点,你是怎么解决的?node

其实这个问题旨在了解你在遇到问题的时候的解决方法,毕竟如今前端技术领域广,各类框架和组件库层出不穷,而业务需求上有时纷繁复杂,观察一个程序员在面对未知问题时是如何处理的,这个过程相对于只出一些面试题来考面试者更能了解面试者实际解决问题的能力git

而不少人会说个人项目不大,并无什么难点,或者说并不算难点,只能说是一些坑,只要google一下就能解决,实在不行请教我同事,这些问题并无困扰我好久。其实我也遇到过相同的状况,和面试官说如何经过搜索引擎解决这些坑的吧不太好,让面试官认为你只是一个API Caller,可是又没有什么值得一谈的项目难点程序员

个人建议是,若是没有什么能够深聊的技术难点,不妨在平常开发过程当中,试着封装几个经常使用的组件,同时尝试分析项目的性能瓶颈,寻找一些优化的方案,一样也能让面试官对你有一个总体的了解github

上篇分享了我在项目中是如何根据功能划分模块以及性能优化的技巧,这章我会记录设计和封装组件的过程面试

技术栈是Vue + Element的单页面应用后端

封装组件

有人会说,github上那么多好的开源组件,好的开源库放着不用,为啥本身还要折腾呢?api

其实我认为本身动手封装一个组件仍是颇有意义的,由于若是是从零开始编写的组件,你可以更好的掌握本身组件的全部功能,而且还能根据公司的业务需求定制一些特殊的功能,除此以外,理解一个组件内部的实现机制也有助于提高我的的编码能力,而不是别人问起来你只知道我用过某个组件,很好用,可是不知道是怎么作到的。因此我仍是比较推荐去尝试编写几个经常使用的组件数组

由于是后台管理系统,核心的组件确定是表单组件和表格组件,公共组件是基于element组件的二次封装,组件的设计遵循如下的思路

  • 高内聚低耦合,尽量少的暴露组件的api,将功能尽可能封装在组件内部
  • 组件内部根据业务需求设置了一些组件默认的配置项,另外再经过不一样页面传入不一样配置项提升组件的通用性

设计组件的目的就是让组件进一步解耦,将配置项和模板标签分离,一方面是减小在业务逻辑组件中的代码量,另外一方面就是单独抽离的配置项使得可以经过后台动态传递给前端,或者本身建一个配置项的js/ts文件(若是有规范的开发者文档还可使用nodejs编写一个读取开发者文档一键写入配置项的脚本,进一步提高开发效率)

表格组件

表格组件设计大体分为如下几个部分

  • 尽可能贴近element组件库的api
  • 传入一个表头的配置项数组,经过这个配置项动态生成el-table-columns标签
  • 交互复杂的表头列的解决方式

尽可能贴近element组件库的api

组件中使用了$attrs,$listeners实现属性和监听事件的跨级传递,使得在页面中给自定义组件中的传入的属性可以经过自定义组件内部的转发直接成为el-table标签的属性,达到跨级的属性传递,而$listeners和$attrs相似,可以监听el-table组件中触发的事件,将事件转发 到页面中的自定义组件上

自定义组件:

这样作的目的就是能让你的自定义组件和el-table组件有相同的用法,传入的属性,监听的事件也是相同的

在页面中使用自定义组件,能够看到z-table和el-table的用法几乎是相同的,只须要额外传入一个columns的属性:

表头的配置项设计

继续给这个表格组件添加表头标签,这里我把一些没必要要的判断都去除了,只留下了核心的逻辑,实际在组件内部只须要循环这个配置项动态生成el-table-column标签就能够了

自定义组件:

配置项文件:

这里的核心是在于这个v-bind,当v-bind后面等号里放入的是一个对象时,它会遍历这个对象的全部属性,将属性和值一一作绑定

什么意思呢?这里结合配置项文件来讲明,若是传入上述的配置项,组件的内部实际是这样子的

抛开key不谈,在配置项的每一个元素中暴露一个attrs属性,里面保存了全部el-table-column标签能够接受的属性。例子中label,prop,width这3个属性是在配置项每一个元素的attrs属性中的,经过v-bind="column.attrs"让这3个属性和它们的值分别在el-table-column标签中作了绑定,从而达到了模板和配置项解耦的目的

交互复杂的表头列的解决方式

对于一些须要特别处理的表头列的数据,我在组件内部利用插槽和做用域插槽,经过插槽定义表头列的插入位置,再经过做用域插槽将信息返回给父组件,在父组件中定义如何显示,使得表格组件很是的灵活可以应对大部分业务需求

能够看到具名插槽的名字也是经过配置项传入的,而且做用域插槽将整个表单内部的数据经过scope传给父组件,在复杂的业务场景,没法经过配置项解决问题的时候,经过插槽和做用域插槽让父组件去决定如何去处理数据

配置项中添加插槽属性:

页面组件:

在页面组件中,能够和element提供的做用域插槽的使用方式类似,经过scope能够访问到组件内部的全部数据而且交给页面组件去作复杂的逻辑处理

其余功能

针对公司的需求,我对组件作了进一步的改造

  • 使用render函数使得表头显示可以更加灵活
  • 配置项暴露一个函数可以让当前列的数据执行这个函数达到预处理的效果
  • 配置项中设置一个二维数组,可以让数据字段组合,达到数据显示在不一样的行数的效果
  • 添加了操做图标
  • 添加了数据(code码)转对应中文语义的功能

源代码

表格组件

表单组件

表单组件相对于表格组件在实现方面要困难一点,由于表单的控件很是多,每一个配置项又须要很是灵活,这里我借鉴了以前在知乎看到的一篇博客,文章中虽然没有把代码列出来,可是罗列了总体的实现方案,随后我根据文章中的思路设计了这个表单组件

设计大体分为如下几个部分

  • 表单配置项设计
  • 表单验证
  • 表单请求
  • 表单控件之间的联动
  • 调用后端接口生成表单控件的选项

表单配置项设计

根据上面的表格组件的封装思路,仍是利用$attrs作根元素属性的传递,用v-bind在配置项中设置组件内部的属性

表单组件:

配置项文件:

和表格组件不一样的是,由于表单组件分为el-form-item标签和表单控件2部分,这2个部分都须要在配置项中对应配置属性,在配置项中使用itemAttrs控制el-form-item标签的属性,使用attrs控制表单控件的属性

这里还用到了component标签,经过配置项的tag标签动态生成el-input的表单控件,可是能够看到这里我并无直接将tag的值设为el-input,那input是如何变成el-input的呢?

这里我又定义了每一个组件通用的配置项,使得不须要每次都在组件的attrs中声明一些重复的属性,好比placeholder,clearable等

通用配置项文件:

最重要的是我创建了组件配置项和通用配置项之间的关联,经过组件配置项中的tag属性找到通用配置项对应的对象,结合上面的例子若是tag的值是input,那就会从通用配置项中找到input属性对应的对象,而且将真实的tag值指向通用配置项的component,这里就是el-input

而这种关联又是怎么创建起来的呢,其实仍是用了Object.assgin作了对象之间的合并

核心逻辑以下(参数formItem指的是组件配置项formItems中的每一个元素):

这里定义了一个computeFormItem的函数,经过传入配置项数组的每一个元素,根据元素的tag值找到通用配置项(basic对象)中相应的值,随后用了Object.assgin作了合并,关于这个computeFormItem函数我稍后在后面的表单控件之间的联动中会详细去讲

通用和组件配置项都有了,接下来要实现的是表单组件须要上传给后端的数据对象

这里个人思路是经过配置项中声明的字段名(key)动态生成数据对象,这样能够减小传入的配置项的数量,在组件内部声明Model变量保存数据对象

可是这里有2点须要注意

  • 由于组件内部声明的Model是一个空对象,Vue的响应式系统是监听不到对象建立了新的属性,须要使用$set来设置,使得可以强制更新视图
  • 这里须要经过formItems对Model数据对象赋值,若是放在mounted钩子中执行的话拿到的是一个空数组,因此我这里使用watch来监听formItems,而且使用immediate当即执行(用computed声明一个新数组理论上也能够)

表单验证

表单验证方面尽可能贴合element组件的传入方式,保持全部在el-form-item标签中写的属性都写在itemAttrs中,全部在表单控件中写的属性都写在attrs中,因此能够在itemAttrs中编写表单验证方面的逻辑

表单请求

表单请求方面,由于在重构时新建了api文件夹,存放的是一个个后端接口的api函数,作到一个页面对应一个api文件夹中的一个接口文件

每一个接口文件中能够导出多个接口的函数

在表单组件中只须要声明一个api的props让页面组件传入就能够了

随后给提交按钮绑定click事件,进行表单验证最后执行接口函数,传入Model这个数据对象便可

在接口函数调用成功返回响应数据后,我这里经过触发after-submit的事件让页面组件监听这个事件,而且把响应数据传给页面组件,这样页面组件就能拿到响应的数据而且作一些处理了

页面组件监听after-submit事件:

表单控件之间的联动

这一部分我认为也是最难实现的,在平常的业务需求中可能须要某个控件控制另一个控件显示与否

核心的思路就是在配置项中定义一个getAttrs的函数,这个函数根据当前Model,也就是数据对象中的某个值动态的生成一个attrs对象,最后将这个attrs对象经过合并到当前配置项的attrs中,另外还定义了一个ifRender函数,能够控制表单控件是否被渲染,最后咱们的配置项可能长这样

接下来表单组件内部要实现如何执行这2个函数,依旧是以前的computeFormItem这个函数,它用来计算出当前表单组件的配置项

和上面的computeFormItem函数不一样的是,我这里传入了第二个参数Model,使得当前的表单组件配置项可以根据Model动态的变化,在内部执行getAttrs函数传入这个Model,返回的对象经过Object,assgin合并到当前的配置项中,而对于另外一个ifRender函数,也传入Model,返回一个Boolean值,最后用这个Boolean值在模版中经过v-if控制是否渲染表单控件

这里要分析一下整个表单最核心的部分:computeFormItem函数,它的做用是根据当前Model中的数据变化,动态的生成一个新的配置项,由于咱们的表单控件是根据配置项映射而成的,须要改变表单控件只能去修改配置项

根据上面那个图能够发现,咱们并无直接使用页面组件传来的formItems配置项,而是根据_formItems循环渲染的标签,而_formItems是基于formItems而且通过computeFormItem生成的配置项,只要Model中的数据改变,这个配置项就须要从新计算生成新的值,因此我选择把_formItems放在计算属性中

这样,只要依赖项(这里是Model和formItems)变了,就会触发函数从新计算出新的_formItems

下拉框/单选框/复选框

在表单组件中,我使用component标签动态生成表单控件,可是对于一些有子节点的表单控件经过component实现就有些困难,这里我将含有子节点的组件(下拉框/单选框/复选框)又进行了一层封装,消除了子节点,让全部属性都在component这一层配置

自定义select组件

这样以来只要在配置项中声明一个options属性,经过component标签将组件转为自定义的select组件,让options属性就会变为select组件的属性,这样在自定义的select组件内部能够经过$attrs.options获取到它(这里注意value,label必须都要显式的声明不然会报错,由于element组件内部会对传入的属性验证)

组件配置项文件:

这里再次利用通用配置项文件,将组件配置项中声明的select组件的配置项映射到自定义的select组件中

调用后端接口生成表单控件的选项

在真实的业务需求中,部分下拉框,单选框的选项是经过拉取后端的接口生成的。放在表单组件中的话仍是须要修改配置项,在页面组件中修改formItem。找到下拉框/单选框的key,将接口的数据赋值给options属性

总结

能够看到表单组件仍是比较复杂的,其实这个表单组件相对于表格组件来讲仍是有必定的局限性,后续可能会给它设计插槽的功能。另外真实的业务需求确定是更加复杂多变的,无论怎么说,一些交互逻辑不是特别复杂的表单这个组件仍是能hold住的,本人能力有限,这里也只是给一个思路,但愿后续可以愈发完善

源代码

表单组件

一键生成配置项

介绍一款我本身写的工具库,能够和表格组件完美配合,读取开发者文档,一键生成组件的配置项,免除多字段输入的错误和重复劳动,有帮助的话但愿各位赏个 star ~

excel-code-generator

参考资料

加快Vue项目的开发速度

不再想写表单了

相关文章
相关标签/搜索