## 定义
二次封装是指在**原有的组件基础上**,通过**拓展额外的功能、优化参数、统一风格**等方式,对组件进行**再包装**,以提升其**适用性**和**易用性**。
## 代码环境
我通过**遇到问题、讨论问题、提出结论**的方式向大家分享,便于大家思考总结。编程语言选择Vue.js 2.x和HUI,以下是简要的代码结构。
```vue
<!-- 二次封装组件 -->
<template>
<el-input></el-input>
</template>
<script>
export default {
// 定义接收父组件传递参数
props: {
attr1: {
type: String,
default: ''
}
...
}
}
</script>
```
## 传递属性
我们封装组件时,需要在原有的组件基础上拓展,最好不要破坏使用方式及参数传递。可是el-input,支持传递type、placeholder、disabled等参数,总不能挨个定义props属性接收,挨个传递到el-input吧?这样的代码它不优雅,不可取。所以$attrs就是我们的大救星,它是Vue组件实例的属性,通过this.$attrs可以访问到,我们只需要通过v-bind整个绑定透传到el-input。这样我们使用组件时,只需要传递我们拓展的参数即可,代码如下所示:
```vue
<!-- 二次封装组件 -->
<template>
<el-input v-bind=“$attrs”></el-input>
</template>
```
这时候又出现一个问题,我想拓展el-input原生的属性,我该怎么办?很简单,我们只需要在props定义对应的参数,这样该参数就不会往下透传,代码如下所示:
```vue
<!-- 二次封装组件 -->
<template>
<el-input v-bind=“$attrs” :type="type"></el-input>
</template>
<script>
export default {
props: {
// 只要定义type参数,那么父组件传递的type将不会透传,需要我们手动传递。
type: {
type: String,
default: 'new type'
}
}
}
</script>
```
## 传递插槽
el-input支持传入插槽,例如:prefix、suffix,我们二次封装的时候,也是不能写死的,所以借助Vue组件实例的$slots,能够拿到父组件传递的插槽,所以我们遍历它,定义插槽,然后传递作用域,代码如下所示:
```vue
<!-- 二次封装组件 -->
<template>
<el-input v-bind=“$attrs”>
<template v-for="(value, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
</el-input>
</template>
```
## 传递事件
el-input支持监听事件,例如:select、change,我们二次封装的组件,也要保持监听回调方式一致,那么有请$listeners,我们只需要透传即可,这样我们封装的组件也能愉快的使用@change监听回调,非常方便,代码如下所示:
```vue
<!-- 二次封装组件 -->
<template>
<el-input v-bind=“$attrs” v-on="$listeners">
<template v-for="(value, name) in $slots" :key="name" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
</el-input>
</template>
```
## ref实例方法
因为我们二次封装el-input,所以通过ref拿到组件实例,调用暴露的方法就非常困难,很难写出适用性高的代码。在Vue.js领域中,没有太优雅的解决方案,我认为有两个解决方案:(1)约定使用,告知大家通过rootRef属性,获取原生组件的ref;(2)方法合并,把原生组件的方法合并到二次封装组件的实例上。这里我给大家两个示例,大家按照喜好定夺,代码如下所示:
```vue
<!-- 二次封装组件 -->
<template>
<el-input ref='root' v-bind=“$attrs” v-on="$listeners">
<template v-for="(value, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
</el-input>
</template>
<script>
export default {
mounted() {
// 1.规约使用方案
// this.rootRef = this.$refs.root
// 2.合并方法(推荐)
const entries = Object.entries(this.$refs.root)
for(const [key, value] of entries) {
this[key] = value
}
}
}
</script>
```
## 结尾
经过上面的分享,相信大家能够有效地封装好Vue.js组件,使其更好地服务于项目需求。 希望这篇经验案例对大家有所帮助!
