由于疫情的原因啊,我猜大多数人都跟我一样,在家里度过,偶尔学习学习。这不,我遇到了一个如何手动一个v-model的问题,由此展开了学习。
在开始之前我们先需要知道一些知识,v-model是一个语法糖,如下:<input type="text" v-model="name">
实则是等于下面的:<input type="text" :value="name" @input="name=$event.target.value">
也就是说,v-model就是绑定了一个名为value的props和一个事件input
注意:所以在子组件中可以通过props中定义value接受值,然后通过$emit触发input事件传新值并修改。
知道了这个知识之后啊,那么我们就开始实现一个自定义的v-model啦~~
一、最简单的实现方法
首先是父组件的代码:
<template>
<div class="">
<p>{{name}}</p>
<son v-model="name"></son>
<!-- 等同于下面 -->
<!-- <son type="text" :value="name" @input="name=子组件传回来的值"> -->
</div>
</template>
<script type="text/javascript">
import Son from './son'
export default {
name: "",
data() {
return {
name:'ydw',
}
},
components: {
Son
}
}
</script>
然后是子组件的代码:
<template>
<div class>
<input type="text" :value="value" @input="$emit('input',$event.target.value)">
</div>
</template>
<script type="text/javascript">
export default {
name: "",
props:{
value:{
type: String,
defalut ()
return ''
}
}
},
data() {
return {
}
},
components: {
}
}
</script>
父组件中我加了一个注释的代码行,其实是帮助我自己理解的,不知道有没有小伙伴跟我一样,第一次看的时候压根不知道它在做什么的?哭唧唧。
二、使用watch和$emit实现
跟上面的一个有些类似,只不过上面是用本身input事件的作用,通过input事件的频发触发传递值给父组件,这里只是将这个过程换成了通过watch监听值得变化,然后通过input事件触发函数事件,函数事件里提交变化的值给父组件。好了,我只需要修改子组件,如下:
<template>
<div class>
<input type="text" :value="sonVal" @input="handleModel" />
</div>
</template>
<script type="text/javascript">
export default {
name: "",
props: {
value: {
type: String,
defalut() {
return "";
}
}
},
data() {
return {
sonVal:this.value
};
},
watch: {
value(newval) {
this.sonVal = newval
}
},
methods:{
handleModel(e){
this.sonVal = e.target.value
this.$emit('input',this.sonVal)
}
}
};
</script>
通过watch监听props值得变化,赋值给子组件本身定义的sonVal,绑定在input里。然后利用input事件触发函数,提交到父组件。
三、使用最新的语法,model属性。
本来我是不知道有这个属性的,就是在网上搜索关于v-model实现方法的时候,就不知不觉的查到这个属性,查看官方文档如下:
也就是说这个属性是为了回避props定义的value这个名字的。
下面看看它的用法,我们先看子组件怎么修改:
<template>
<div class>
<input type="text" :value="val" @input="$emit('change',$event.target.value)" />
</div>
</template>
<script type="text/javascript">
export default {
name: "",
model:{
prop:'val',
event:'change'
},
props: {
val: {
type: String,
defalut() {
return "";
}
}
},
data() {
return {
};
}
};
</script>
model属性中prop是重新命名传值的名字,而event则是重新命名事件的名字,但是同样的,props中的传值名字必须与model属性中的prop的名字一样。其实只要对比一下第一种方法就知道,vue多出一个model属性是为了value这个名字可以使用。
同样的,这里也贴出使用新属性后,在使用wacth是什么样的,也是只是修改了son组件
<template>
<div class>
<input type="text" :value="sonVal" @input="handleModel" />
</div>
</template>
<script type="text/javascript">
export default {
name: "",
model:{
prop:'val',
event:'change'
},
props: {
val: {
type: String,
defalut() {
return "";
}
}
},
data() {
return {
sonVal:this.val
};
},
watch: {
value(newval) {
this.sonVal = newval
}
},
methods:{
handleModel(e){
this.sonVal = e.target.value
this.$emit('change',this.sonVal)
}
}
};
</script>
其实也没多大的修改,只是想贴出来而已。那么手写一个v-model我就写完了,觉得有用的同志给我点个赞赞赞赞赞赞呗。
分割线
距离文章发布已经有半年多的时间了,最近用vue2.0封装elementUI,在封装form表单的时候恰巧用上了这个吃灰许久的东西记录一下。
首先讲我简单的封装代码上来:
<template>
<div class="form-container">
<el-form
:model="filterObject"
label-position="left"
:label-width="labelWidth"
>
<el-row :gutter="10">
<el-col
v-for="item in formData"
:key="item.prop"
:span="item.span || 8"
>
<el-form-item :label="item.label">
<el-input
v-if="item.type === 'text'"
v-model="filterObject[item.prop]"
clearable
:disabled="item.disabled || false"
:placeholder="item.placeholder"
></el-input>
<el-select
v-if="item.type === 'select'"
v-model="filterObject[item.prop]"
clearable
:placeholder="item.placeholder"
@change="handleChange"
>
<el-option
v-for="sub in item.arr"
:key="sub.value"
:label="sub.label"
:value="sub.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
export default {
model: {
prop: 'filterObject',
event: 'change',
},
props: {
filterObject: {
type: Object,
default() {
return {}
},
},
labelWidth: {
type: String,
default: '80px',
},
formData: {
type: Array,
default() {
return []
},
},
},
data() {
return {}
},
methods: {
handleChange() {
console.log(this.filterObject)
},
},
}
</script>
复制代码
其中的filterObject就是今天的主角了,我的想法是在父组件直接传递一个对象进来,在进行相关操作的时候,父组件直接可以在外面用到这些值,而不是通过子组件暴露方法获取这些值。(ps:在封装search组件的时候可以这么做,向外暴露search,reset方法可以暴露子组件里面的值。其实封装search跟这个form有点相似,都是用elementUI的form表单来封装的。)
接下来是父组件的代码:
<template>
<div class="home">
<HHform
:formData="searchOptions"
labelWidth="120px"
v-model="filterObject"
></HHform>
</div>
</template>
<script>
import HHform from '@/components/HH-form'
export default {
name: 'Home',
components: {
HHform,
},
data() {
return {
searchOptions: [
{
type: 'select',
prop: 'status',
label: '状态:',
span: 5,
placeholder: '请选择',
arr: [
{
value: '',
label: '全部招生顾问',
},
{
value: 1,
label: '正常招顾',
},
{
value: 2,
label: '冻结招顾',
},
],
},
{
type: 'select',
prop: 'region',
label: '地区:',
span: 5,
placeholder: '请选择',
arr: [],
},
{
type: 'text',
prop: 'userName',
span: 8,
label: '招生顾问名称:',
placeholder: '选择或输入搜索',
},
{
type: 'text',
prop: 'phone',
label: '手机号:',
span: 5,
placeholder: '选择或输入搜索',
},
],
filterObject: {},
}
},
methods: {},
},
}
</script>
复制代码
父组件的代码比较简单,直接通过v-model传值过去就行了。下面是演示效果。
目前封装form组件的时候是使用这种方法,父组件直接传值的话,不需要子组件提供方法返回值。在封装search组件的时候则是使用子组件暴露方法的方式拿到值的,为什么这么做呢?因为在进行搜索的时候一定会暴露search跟reset方法,这时候带出去就行了。时隔多个月终于是用上的东西,特地记录下来,希望可以帮助正在封装组件的你。喜欢的可以点个赞或者收藏呗。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。