前言
假如有个需求,输入框在输完内容后进行判断是否为纯空格,如果是纯空格,就要清空输入框,对于el-form
组件的表单验证,在rules
中的required
为true
时,纯空格也能检验通过,于是开始对el-input组件进行二次封装。
1、一般说到二次封装,很多人是这样封装的,我以前也是:
- 父级
main.vue
<template>
<my-input v-model="value"></my-input>
</template>
<script>
import MyInput from './my-input.vue;
export default {
components: {
MyInput
},
data() {
return {
value: ''
}
}
}
</script>
- 子组件
my-input.vue
<template>
<el-input></el-input>
</template>
你会发现这个输入框你不能输入任何东西,因为子组件my-input并没有支持任何传参,于是接着改:
<template>
<el-input :value="value" @input="handleInput" @change="handleChange"></el-input>
</template>
<script>
export default {
props() {
value: [String, Number]
},
methods: {
handleInput() {
this.$emit('input', $event)
},
handleChange(value) {
//处理纯空格
if (Object.prototype.toString.call(value) === '[object String]' && value.trim() === '') {
this.$emit('input', '')
}
}
}
}
</script>
如果要全部输入框都支持“不允许输入纯空格”,就将它封装成全局组件,看似完美,但是你会发现,el-input组件除了可以进行值的双向绑定,它还支持size、readonly、disabled...等18个属性,7个事件,于是接着写一大堆“中间代码”来继承原有el-input的各种属性、事件。
2、改进: 利用 v-bind="$attrs"
和 v-on="$listeners"
来“继承” el-input
组件的属性和事件
<template>
<el-input v-bind="$attrs" v-on="$listeners" @change="handleChange"></el-input>
</template>
<script>
export default {
methods: {
handleChange(value) {
if (Object.prototype.toString.call(value) === '[object String]' && value.trim() === '') {
this.$emit('input', '')
}
}
}
}
</script>
如果将这样的组件覆盖原有的el-input,那就会出问题,它并不支持插槽...,有想翻桌子的冲动。那么,有没有一种只想改我想改的部分,其它的不动,不需要自己手写代码去“继承”呢?
3、使用 extends 选项对组件进行扩展
- 雏形
<template>
//这里复制element-ui中el-input的源码,太长了,不好展示
</template>
<script>
import { Input } from 'element-ui'
export default {
extends: Input,
created() {
this.$on('change', (value)=> {
if (Object.prototype.toString.call(value) === '[object String]' && value.trim() === '') {
this.$emit('input', '')
}
})
}
}
</script>
这样就能对组件各种改改改;
template里面的内容是复制过来的,一般不需要改动,如果需要改的话当然也可以这么做。
- 改进
如果不需要template
,那就写在“*.js”
文件中
import Vue from 'vue';
import ElementUI from 'element-ui';
Vue.component('el-input', {
extends: ElementUI.Input,
created() {
this.$on('change', (value)=> {
if (Object.prototype.toString.call(value) === '[object String]' && value.trim() === '') {
this.$emit('input', '')
}
})
}
})
这下简洁多了!
- 局部组件
<template>
<div>
内容...
<my-input></my-input>
</div>
</template>
<script>
import { Input } from 'element-ui'
export default {
components: {
myInput: {
extends: Input,
...
}
}
}
</script>