Vue2基于Element封装自定义组件的方案设计

1,377 阅读7分钟

element-ui是一款好用的基于vue的一个UI框架,得益于此类UI框架和组件的出现,使得我们前端开发人员在工作过程中能够拿来即用,减少了造轮子的时间,(主要是减少加班时间),但在此基础上,有些小伙伴为了能更加减少加班时间,往往会选择,在基于UI框架基础上,封装一些自定义的组件,将一些常用的设置、方法包含进去,从而减少代码量和重复的复制粘贴操作。基于此,本文想谈一下,在基于element-ui封装自定义组件的时候,有哪些技巧可以使用。

梳理逻辑

当我们想要做一个新的东西的时候,我们无非是从”做什么“、”怎么做“,”如何做“这些基础的问题出发的,封装组件也不例外。

1. 做什么

个人认为,当我们想要去封装一个新的组件,尤其是在已有的轮子上面进行优化封装的时候,一定是因为想抛弃或者优化某些繁琐的操作,或者原有组件的基础功能不能满足一些高阶功能,那么这些操作,就是封装组件时候要做的内容。

el-table组件为例,在使用的时候,要根据数据的组成和需要展示的内容,分别写出一个个的el-table-column来展示每列数据,这种操作繁琐且代码行数冗余,当在另外一个场景中同样用到el-table组件时,常常是把el-table-column列复制过去进行参数的修改,或者重新写el-table-column,无论是哪种操作,都会增加我们书写代码的繁琐度,那么在封装组件时,就可以把减少el-table-column或者取消el-table-column的书写作为一项切入点,由此,我们就识别到了要封装的组件的一个需求点,同样的方式,识别出组件的其他需求点,那么”组件要做哪些东西“这个问题就得到了解决。

2. 怎么做

在第一步识别出组件的需求之后,下一步就要根据一个一个的需求点进行方案的设计,也就是怎么去实现需求的问题,针对上文的例子,要减少用户el-table-column或者取消el-table-column的书写,那就需要将el-table-column的书写和渲染逻辑放到组件内部实现,很自然地,我们想到可以通过传参的形式,将列数据以json串的形式传入组件内部,组件内部通过for循环的方式遍历数据并生成对应的el-table-column列,至此就完成了这个需求点的基本功能。

// todo 放图

在此基础上,我们不禁要思考,在渲染表格数据的时候,有时候不仅仅单纯地展示数据,而是要进行一个额外的处理,比如说在数据前面加上图标,那么这时,刚刚的处理方式就不能满足这个需求了,element的官网给出的示例是通过插槽来使得用户可以自定义列模板,那么我们在封装组件的时候就可以参照此做法,也预留出插槽使得用户能够自定义想要的列模板。

image.png 这只是标识出的一个简单的场景一,在封装组件的时候,根据一个需求点,结合一些具体的业务需求,尽可能多的识别出其他的场景二、场景三,……然后不断地去完善这个需求点。

3. 如何做

前两步可以说是需求收集和方案设计阶段,那么到了这一阶段,就是要进行实际编码的阶段了,在这个过程中,我们更多的是要考虑技术层面的问题,也就是通过具体的代码去实现上面的方案设计。下面记录的是在编写组件过程中关于传参的一些设计和技巧。

设计传参

  1. $attrs$listeners

在实操组件过程中,对于固定的操作,以及无需用户关心的操作,通常是直接写个固定值在组件内部,而那些需要用户自定义的操作则采用父子通信的传参方式,通过prop方式接受外部传参,通常如果用户不传时,组件会设置一个默认值以达到组件的默认效果。然而,我们不得不考虑的一个问题是,el-table自身有很多入参,实现的效果也很多样,如果我们只期望封装出来的组件只具备el-table一部分功能的时候,可以只将这部分功能涉及到的参数以prop方式传入,但是如果想在封装一部分功能的基础上保留el-table组件的其他功能,那么这时,如果再一个一个地将参数以prop参数传入,整个组件的prop就会变得异常庞大,无用代码量也会增多。这时,不妨考虑一下使用v-bind="$attrs"v-on="$listeners"

以下是来自vue官网的关于$attrs$listeners的介绍:

$attrs:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

可见,v-bind="$attrs"v-on="$listeners"是我们在原有组件基础上进行高阶组件封装的一个很好的利器,用户将一些el-table的参数和方法传入到我们封装的组件内部,封装的组件不对这些参数和方法作额外的处理,而直接将这些内容继续传入到el-table内部,el-table再对这些参数和方法作出具体的反应,不难看出,我们封装的自定义组件在这个过程中只充当了一个中间件的作用,用来接收外部参数并转发给el-table,从而能保留el-table的这些自身操作。

最后总结,在封装组件时,对于组件所封装和关注的部分的传参以prop形式传入组件内容,而组件不关注或者不封装的部分,可以通过设置v-bind="$attrs"v-on="$listeners"是保留element-ui组件的自身功能。

  1. .sync

如果将组件称为子组件,调用组件的部分称为父组件的话,那么父组件通过prop方式将参数传入到组件内部,组件通过$emit方式将方法暴露给父组件,或者通过ref对子组件做标记,父组件可以通过vm.$refs.[子组件的ref].[子组件的属性/方法]这种方式直接取得子组件的数据,这也是一般父子组件进行通信的方式,但是从子组件向父组件传递数据时,父子组件中的数据仍不是每时每刻都同步的,但在某些特殊的需求场景下,我们可能会希望父子组件中的数据时刻保持同步,以el-pagination组件为例,current-pagepage从外部传入,当切换页码和改变每页数量时,我们期望外部传入的current-pagepage能同步更新,此时,.sync就派上了用场。官网关于 .sync的介绍

发布组件

组件封装完成之后,可以通过发布到npm仓库的方式供其他人使用,发布npm包的方式大家可自行百度。在发布npm包的时候,还需要注意的两个点是:版本号和文档。版本号的命名和更新是极其重要的,不能忽略,在后续的发布和维护时,也要注意版本号的设置,不能随意修改。对于文档,可以说是组件的门面,用户一般都是通过文档来使用组件的,文档的书写与否和文档的详细程度能直接影响到用户对组件的使用率和评价。好的文档也能增加开发者的底气,比如说,如果有人吐槽你的组件没有某个功能不好用,而实际上组件是有这个功能并且文档中已经详细介绍这个功能的时候,你就可以优雅地这样回复他。

image.png