输入框组件封装
作者:Yuan Tang
更新于:5 个月前
字数统计:1.1k 字
阅读时长:4 分钟
v-model的本质
v-model
的本质上是 :value
渲染变量、@input
输入框修改事件触发后通过 $emit
把新的值传给父组件的语法糖。父组件通过 v-model
直接实现双向绑定。
子组件:
vue
<script>
export default {
props: {
value: String | Number,
},
methods: {
inputFn(e) {
this.$emit('input', e.target.value)
},
},
}
</script>
<template>
<div>
<input :value="value" @input="inputFn">
</div>
</template>
注意
$emit
子传父中的事件名也要是input
。
父组件:
vue
<script>
import MyInput from '@/components/MyInput.vue'
export default {
components: {
MyInput
},
data() {
return {
value: '',
}
},
methods: {
},
}
</script>
<template>
<div id="app">
<MyInput v-model="value" />
{{ value }}
</div>
</template>
此时运行代码,封装的输入框组件可以通过 v-model
双向绑定 value
变量。
组件封装的思考
输入框组件的封装需要考虑到以下几点:
- 输入框类型
- 类名
- 最大输入的长度或数量
- ......
其中,输入框类型我们是可以确定用户只能输入什么才能生效的,如用户输入 text
、number
、password
就可以生效,输入其他字符串如 abc
则无法生效,因此可以用到 props
的校验属性 validator
。
作为一个封装的组件,我们无法估量父组件在使用时会用到什么场景功能,因此,原生结构有啥方法我们就向外 $emit
什么方法,提供给父组件使用。
vue
<script>
export default {
props: {
type: {
type: String,
validator(value) {
return ['text', 'textarea', 'number', 'password'].includes(value)
},
},
value: String | Number,
},
methods: {
inputFn(e) {
this.$emit('input', e.target.value)
},
changeFn(e) {
this.$emit('change', e.target.value)
},
focusFn(e) {
this.$emit('focus', e.target.value)
},
blurFn(e) {
this.$emit('blur', e.target.value)
},
},
}
</script>
<template>
<div class="input-wrap" :class="{ 'wrap-position': searchAction }">
<input :value="value" :type="type" @input="inputFn" @focus="focusFn" @blur="blurFn" @change="changeFn">
</div>
</template>
模糊搜索
搜索
在用户输入后可实现搜索功能,作为一个组件,要考虑两种情况:
- 用户只传一个布尔值表示需要输入搜索,就直接返回一个
$emit
方法,让父组件处理(不简便,但是能应对特殊情况) - 用户给入一个
url
,然后自动取请求url
地址,拿到模糊查询结果显示(无法应对特殊调整)
通过传入一个变量 searchAction
来辨别,默认给一个 false
。
vue
<script>
// 1.只需要给如url,然后自动取请求url地址,拿到模糊查询结果显示(无法应对特殊调整)
// 2.如果searchAction不传url而是布尔值,则返回一个$emit方法,让父组件处理(不简便,但是能应对特殊情况)
export default {
props: {
// ...
searchAction: {
type: Boolean | String,
default: false,
},
},
methods: {
inputFn(e) {
this.$emit('input', e.target.value)
if (this.searchAction) {
// 模糊查询
this.search(e.target.value)
}
},
search(e) {
if (typeof this.searchAction === 'string') {
// 如果是字符串,则子组件直接调接口
axios.get(`${this.searchAction}?v=${e}`).then((res) => {
this.localSearchData = res.data
this.$emit('search', res.data) // 考虑万一父组件要使用的特殊情况
})
}
else {
// 如果是布尔值,则父组件自行处理
this.$emit('search')
}
},
},
}
</script>
<template>
<div class="input-wrap" :class="{ 'wrap-position': searchAction }">
<input :value="value" :type="type" @input="inputFn">
</div>
</template>
<style scoped>
.wrap-position {
position: relative;
}
</style>
防抖与节流的区别
像这种输入就查询的频繁操作,要考虑到添加防抖或节流,降低服务器的压力,此场景最合适的还是节流。具体步骤如下:
- 外部定义一个变量
timer
- 触发输入查询事件后判断
timer
是否有值 - 有值清空定时器,并把新的定时器赋值给
timer
vue
<script>
// 1.只需要给如url,然后自动取请求url地址,拿到模糊查询结果显示(无法应对特殊调整)
// 2.如果searchAction不传url而是布尔值,则返回一个$emit方法,让父组件处理(不简便,但是能应对特殊情况)
let timer
export default {
props: {
// ...
},
methods: {
inputFn(e) {
this.$emit('input', e.target.value)
if (this.searchAction) {
// 模糊查询
if (timer) {
clearTimeout(timer)
timer = setTimeout(() => {
this.search(e.target.value)
}, 100)
}
}
},
// ...
},
}
</script>
<template>
<div class="input-wrap" :class="{ 'wrap-position': searchAction }">
<input :value="value" :type="type" @input="inputFn">
</div>
</template>
<style scoped>
.wrap-position {
position: relative;
}
</style>
后续把查询出来的值渲染到页面上或者子传父给父组件做处理即可。
表单验证
总结
- 组件封装最大程度要让父组件使用便利,内部最大程度做一定的封装
- 要适时给予一定的拓展功能,让父组件在使用时也能自定义其他功能来适配不同的场合