输入框组件封装
刀刀
4/8/2025
0 字
0 分钟
- UP主:三十的前端课,视频地址:前往学习
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>
vue
<template>
<div id="app">
<MyInput v-model="value" />
{{ value }}
</div>
</template>
<script>
import MyInput from "@/components/MyInput.vue";
export default {
data() {
return {
value: "",
};
},
components: {
MyInput,
},
methods: {},
};
</script>
此时运行代码,封装的输入框组件可以通过 v-model
双向绑定 value
变量。
注意
$emit
子传父中的事件名也要是 input
。
组件封装的思考
输入框组件的封装需要考虑到以下几点:
- 输入框类型
- 类名
- 最大输入的长度或数量
- ......
其中,输入框类型我们是可以确定用户只能输入什么才能生效的,如用户输入 text
、number
、password
就可以生效,输入其他字符串如 abc
则无法生效,因此可以用到 props
的校验属性 validator
。
作为一个封装的组件,我们无法估量父组件在使用时会用到什么场景功能,因此,原生结构有啥方法我们就向外 $emit
什么方法,提供给父组件使用。
vue
<script>
export default {
props: {
type: {
type: String,
validator: function (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': true, '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': true, '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': true, 'wrap-position': searchAction }">
<input :value="value" :type="type" @input="inputFn" />
</div>
</template>
<style scoped>
.wrap-position {
position: relative;
}
</style>
后续把查询出来的值渲染到页面上或者子传父给父组件做处理即可。
表单验证
总结
- 组件封装最大程度要让父组件使用便利,内部最大程度做一定的封装
- 要适时给予一定的拓展功能,让父组件在使用时也能自定义其他功能来适配不同的场合