概括
其实v-model是个语法糖。
v-model 常用于表单元素上进行数据的双向绑定,比如 <input>。除了原生的元素,它还能在自定义组件中使用。可以拆解为 props: value 和 events: input。就是说组件必须提供一个名为 value 的 prop,以及名为 input 的自定义事件,满足这两个条件,使用者就能在自定义组件上使用 v-model。比如下面的示例,实现了一个数字选择器:
<template><div><button @click="increase(-1)">减 1</button><span style="color: red;padding: 6px">{{ currentValue }}</span><button @click="increase(1)">加 1</button></div></template><script>export default {name: 'InputNumber',props: {value: {type: Number}},data () {return {currentValue: this.value}},watch: {value (val) {this.currentValue = val;}},methods: {increase (val) {this.currentValue += val;this.$emit('input', this.currentValue);}}}</script>
props 一般不能在组件内修改,它是通过父级修改的,因此实现 v-model 一般都会有一个 currentValue 的内部 data,初始时从 value 获取一次值,当 value 修改时,也通过 watch 监听到及时更新;组件不会修改 value 的值,而是修改 currentValue,同时将修改的值通过自定义事件 input 派发给父组件,父组件接收到后,由父组件修改 value。所以,上面的数字选择器组件可以有下面两种使用方式:
<template><InputNumber v-model="value" /></template><script>import InputNumber from '../components/input-number/input-number.vue';export default {components: { InputNumber },data () {return {value: 1}}}</script>
或:
<template><InputNumber :value="value" @input="handleChange" /></template><script>import InputNumber from '../components/input-number/input-number.vue';export default {components: { InputNumber },data () {return {value: 1}},methods: {handleChange (val) {this.value = val;}}}</script>
如果你不想用 value 和 input 这两个名字,从 Vue.js 2.2.0 版本开始,提供了一个 model 的选项,可以指定它们的名字,所以数字选择器组件也可以这样写:
<template><div><button @click="increase(-1)">减 1</button><span style="color: red;padding: 6px">{{ currentValue }}</span><button @click="increase(1)">加 1</button></div></template><script>export default {name: 'InputNumber',props: {number: {type: Number}},model: {prop: 'number',event: 'change'},data () {return {currentValue: this.number}},watch: {value (val) {this.currentValue = val;}},methods: {increase (val) {this.currentValue += val;this.$emit('number', this.currentValue);}}}</script>
在 model 选项里,就可以指定 prop 和 event 的名字了,而不一定非要用 value 和 input,因为这两个名字在一些原生表单元素里,有其它用处。
