您现在的位置是:网站首页> 编程资料编程资料

vue二次封装一个高频可复用组件的全过程_vue.js_

2023-05-24 341人已围观

简介 vue二次封装一个高频可复用组件的全过程_vue.js_

前言

在我们的业务里,我们通常会二次封装一些高频业务组件,比如弹框,抽屉,表单等这些业务组件,为什么要二次封装?我们所有人心里的答案肯定是,同样类似的代码太多了,我想复用组件,或者原有组件可能达不到我想要的效果,我想基于原有组件自定义一些自己的接口,那么此时就需要二次封装了。二次封装虽好,但同时也会带来一定的心智负担,因为二次封装的组件可能会变得不那么纯粹。

本文是一篇笔者关于二次封装组件的思考,希望看完在项目中有所思考和帮助。

正文开始...

在内容开始之前,本文主要从以下几个方向去思考:

1、二次组件必须继承原有组件的所有特性

2、二次组件名必须见名知意

3、自定义暴露出来的接口越简单越好

4、留有自定义插槽,让用户可以自己选择

5、封装二次的组件,能根据schame数据配置,让组件更通用

继承原有组件接口

在之前的项目例子中,我们以一个弹框组件为例

我们看下在业务中一般是怎么写的

我们再继续看下list-modal这个组件

我们会发现,这个list-modal业务组件只是包了一层,当我们使用v-bind="$attrs"时,vue提供的这个api会将父组件所有的props继承,官方给了一大段解释

  • $attrs

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

首先我们思考为什么要用这个$attrs?上面一段话的意思是,父组件classstyle会排除

我们从页面上可以看出titlewidth都是父组件传过来的,但是我们发现,实际上这两个外部看似自己传入的props也是el-dialogprops,所以说我们必须要保持自己二次封装的组件也有el-dialog所有能力,所以此时v-bind='$attrs'就可以做到了

  • $listeners

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

在以上的$attrs我们是将父级的所有的props都拿到了,但是自定义事件呢,所以才有的了$listeners

所以你在父组件写了一个el-dialog的自定义事件想要生效,那么必须在子组件绑定$listeners

 ... 

正常来说一个高阶二次组件必须要有v-bind="$attrs"v-on="$listeners"

另外我们自己封装的二次组件里有v-model='formParams'

这个formParams就是我们弹框内部表单的使用内容

v-model

关于v-model实际上官方解释就是用在组件或者表单上创建双向绑定,如果把v-model看成是一个内部提供的一个语法糖,那么它可以拆解成:value="value":input=“handleInput”,v-model不仅仅是可以作用在表单元素上,并且还可以作用在组件上,同时也提供了一个model的接口,提供自定义修改事件名称

以上代码就自定义了modelevent,prop就是formParams,同时props上必须有引入formParams

不知道你有没有好奇,为啥我data中定义了一个currentVisible,而且watchvisiblecurrentVisible,使用currentVisible时,这里是有一个坑,因为弹框的icon关闭操作不会触发最外层事件,也就是你点击右上角的关闭操作后,当你再次打开时,此时,就打不开了,所以就没直接用visible了,我们需要另一个变量,然后去watch最终达到我们需要的效果。

在这里有人会奇怪,传入子组件的formParams直接在表单上使用了,嘿,这样不是直接修改props吗,但实际上控制台并不会报错,如果你父组件传入的是一个基础数据类型,你在子组件里修改是会直接警告你不能修改的,但是你传入的是一个对象,你此时修改的是对象属性值,并没有修改原对象,所以一个非基础数据类型数据,修改内部值时,是不会警告的,这样做也是ok的。

插槽

在这个弹框中的确认和取消操作是用插槽slot="footer"去显示的,如果你想自定义插槽,那么你可以通过具名插槽进行兼容处理

 ... 取 消确 定

在我们的业务中有大量这样的XXXModal弹框,如果我们只是这样包了一层,那么我们只是完成了组件的基本使用,也是符合我们常规业务需求,但是你会发现,我们绝大部份业务里的弹框内容都是表单,所以我能不能通过可配置的schame数据去配置出来呢?

组件更抽象

我们在components下新建了一个form-modal组件,并注册成全局组件,我的目标是把弹框的内容区域做成可配置化,这样我只需要用配置数据就可以渲染出对应的内容

全局注册

// src/components/index.js import Vue from 'vue'; import FormModal from './form-modal'; const custCompoment = { FormModal, }; export const installCustComponent = () => { Object.keys(custCompoment).forEach((key) => { Vue.component(key, custCompoment[key]); }); };

main.js

// main.js import { installCustComponent } from '@/components'; installCustComponent(); ...

我们发现在模版里面有不少添加条件,实际上,这些条件主要根据你业务需要而定,除了模版方式,插槽,我们也可以预留一个自定义formater的接口,像下面这样

...

那么此时你会发现有一个renderComponent这样的自定义组件,我们必须引入进来

/* src/components/form-modal/view/render.js*/ export default { functional: true, props: ['value'], render(h, ctx) { const { formater, attrs, input: handleInput } = ctx.data.attrs; return formater(h, { attrs: { ...attrs, value: ctx.props.value, }, on: { input(e) { handleInput(e); }, }, }); }, };

form-modal/view/index.vue中我们必须引入,所以模版中就可以使用了

我们再看下我们之前业务弹框与schame再次抽象后的两个组件,其实第二个全局组件就多了一个formConfig属性,我们统一把内容抽离了出去,我们的form-modal就变得更加通用,我们只需要关注formConfig这份配置数据就行

/* eslint-disable func-names */