v-model原理
你可以用 v-model 指令在表单 <input>、<textarea> 及 <select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
以上是官方的讲解,其实就是v-bind
与@input
语法糖而已。如下图:
正是通过v-bind
指令将响应式的数据绑定到文本框上,之后再给文本框添加input
事件,当文本框中的数据发生改变的时候,再去改变响应式数据value
的值,进而实现双向数据绑定。
那么,想一下,我们如果实现自定义组件的v-model?
其实在自定义组件的时候,数据流转要比上边介绍的麻烦一些,因为自定义组件必然涉及到父子组件的数据绑定。也就是使用我们组件的地方(父组件)和自定义组件内部(子组件)的通信。
回想一下,父子组件是如何通信的?
如图所示:
- 父组件通过传递Prop给子组件,进而子组件可以使用该数据
- 子组件不可以直接修改父组件,而是需要派发事件,让父组件修改自身属性,子组件进而间接的修改了Prop。
而v-model只需封装一下上边的实现过程即可。同样是语法糖。
如上图,派发的事件为Input,而响应式数据定义为value。
其实,这个语法糖,vue已经帮你实现好了(真贴心),下面的解释摘自官方文档:
允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
注意加粗字体的描述。这其实恰恰就对应着我上图画的模型。既然都有了思路,不妨用代码实现以下:
// 父组件
<template>
<div id="app">
<HelloWorld v-model="foo" />
<div>外层组件:{{ foo }}</div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
components: {
HelloWorld
},
data() {
return {
foo: 1
}
},
}
</script>
// 子组件
<template>
<div class="hello">
<select @change="valueChanged">
<option :value="1" :selected="foo === 1">1</option>
<option :value="2" :selected="foo === 2">2</option>
</select>
</div>
</template>
<script>
export default {
model: {
event: 'change',
prop: 'foo',
},
props: {
foo: {
type: Number
}
},
methods: {
valueChanged(e) {
this.$emit('change', e.target.value)
}
}
}
</script>
在子组件中,我使用了model这个选项,他是vue官方API的一部分。他的作用是改变v-model默认绑定的事件类型
以及prop名称
。我上文引用中有详细的描述。
至此一个自定义的双向数据绑定就实现了 😃 。