v-model本质上是一个语法糖,它负责监听用户的输入事件以更新数据
表单控件的v-model
从表单控制角度来讲,实际上是帮我们做了两件事:
- 给组件默认增加一个属性,值为用户传入v-model的变量,并将它作为双向绑定的承载者,比如 :property="xxx"
- 给组件增加一个监听事件,当监听的值发生改变时,会将监听的值赋予v-model的变量,比如 @listenerEvent="xxx = $event.target.value"
<!-- 这里,以input输入框作为示例 -->
<input v-model="xxx">
<!-- 翻译过来,其实就是下面这句了 -->
<input :value="xxx" @input="xxx = $event.target.value">
看到上面,是不是觉得简单? 哼哼,再来看看官网的用法:https://cn.vuejs.org/v2/api/#v-model
发现v-model并不只是用在input框上了,还用在select等表单控件类型和自定义组件上,我们知道,select的值变化监听是在change事件上了,check-box是要看checked属性是否为true了,还有自定义组件...!
ok,继续看官网 https://cn.vuejs.org/v2/guide/forms.html :
v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用 value property 和 input 事件;
- checkbox 和 radio 使用 checked property 和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
于是乎,我模拟了下input的实现,select的v-model实现的作用大概是这样的:
<select v-model="selected">
<option value ="volvo">Volvo</option>
<option value ="saab">Saab</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>
</select>
<!-- 大致等同于 -->
<select @change="selected = $event.target.options[$event.target.options.selectedIndex].value" :value="selected">
<option value ="volvo" :selected="selected === 'volvo'">Volvo</option>
<option value ="saab" :selected="selected === 'saab'">Saab</option>
<option value="opel" :selected="selected === 'opel'">Opel</option>
<option value="audi" :selected="selected === 'audi'">Audi</option>
</select>
其它组件类似,不再演示,感兴趣的可以自己实现一下
自定义组件的v-model
那么自定义祖件的原理呢,我们再看下官网的解析:https://cn.vuejs.org/v2/guide/components-custom-events.html#%E5%B0%86%E5%8E%9F%E7%94%9F%E4%BA%8B%E4%BB%B6%E7%BB%91%E5%AE%9A%E5%88%B0%E7%BB%84%E4%BB%B6
自定义组件,其实就是使用了input
中的v-model
原理来实现的:
<div>
<!--
相当于
:value="text"
@input="text = $eevent.target.value"
传递给当前组件一个叫value的属性
给当前组件设置了一个自定义事件,input,通过组件内的$emit('input', xxx)来触发事件
-->
<my-input v-model="text"></my-input>
</div>
下面,我们以一道面试题来试手
v-model面试题目
实现一个自定义组件,使用v-model="text"
来实现:text
中的值使用空格隔开,分开用来控制组件内两个输入框的显示,比如text值为"面试题 v-model",组件内实现两个输入框,值分别为“面试题”和“v-model”,然后实现双向绑定
<!-- 实现组件调用 -->
<template>
<div>
<my-input v-model="text" @input="changeText"></my-input>
{{text}}
</div>
</template>
<script>
import MyInput from './MyInput.vue'
export default {
name: 'HelloWorld',
components:{
MyInput
},
data() {
return {
text: '面试题目 v-model'
}
},
mounted() {
setTimeout(() => {
this.text = '不写了 哼'
}, 1000)
},
methods: {
changeText(val) {
this.text = val
}
}
}
</script>
<!-- 组件内部实现 -->
<template>
<div>
<input type="text" v-model="text1" @change="changeInput">
<br>
<input type="text" v-model="text2" @change="changeInput">
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
required: true
}
},
data() {
return {
text1: '',
text2: ''
}
},
methods:{
formatValue() {
let value = this.value.split(' ')
this.text1 = value[0]
this.text2 = value[1]
},
changeInput() {
this.$emit('input', this.text1 + ' ' + this.text2)
}
},
created() {
this.formatValue()
},
watch: {
// 监听一个值,改变多个值
value(newVal, oldVal) {
console.log(newVal, oldVal);
this.formatValue()
}
}
}
</script>
image.png
网友评论