Vue v-model 指令
示例
使用 v-model
指令在 <input>
元素和一个数据属性之间创建双向绑定。
<template>
<h1>v-model Example</h1>
<p>Write something, and see the 'inputValue' data property update automatically.</p>
<input type="text" v-model="inputValue">
<p>inputValue property: "{{ inputValue }}"</p>
</template>
运行示例 »
更多示例请参见下方。
定义和用法
v-model
指令用于在表单输入元素之间,或在 Vue 实例属性和组件之间创建双向绑定。
带 v-model
的表单输入元素
可以与 v-model
一起使用的表单输入元素包括 <input>
、<select>
和 <textarea>
。
在表单输入元素上使用 v-model
进行双向绑定的工作方式如下:
- 当 Vue 检测到输入值发生变化时,它会相应地更新对应的数据属性。(HTML -> JavaScript)
- 当 Vue 检测到 Vue 实例属性发生变化时,它会相应地更新对应的输入值。(JavaScript -> HTML)
(参见上面的示例,以及下面的示例 1。)
带 v-model
的组件
当 v-model
用于组件时,组件接口必须通过 props
和 emits
正确设置,才能实现双向绑定。
在组件上使用 v-model
进行双向绑定的工作方式如下:
- 当 Vue 检测到父实例属性发生变化时,新值将作为 prop 发送到组件。
- 当 Vue 检测到子组件发生变化时,新值将作为一个 emit 事件向上发送到父组件。
当 v-model
用于组件时,默认 prop 名称是 'modelValue',默认 emit 事件名称是 'update:modelValue'。(参见 示例 2 和 示例 3。)
当 v-model
用于组件时,我们不必使用 Vue 实例数据属性,而是可以使用计算属性,并带有 get()
和 set()
方法。(参见示例 4)
与默认的 'modelValue' 和 'update:modelValue' 不同的 prop 和 emit 名称,可以使用 v-model:
进行设置。(参见示例 5)
要将多个值作为双向绑定连接到组件,我们必须为每个这样的值定义自己的 v-model
。(参见示例 6)
修饰符
修饰符 | 详情 |
---|---|
.lazy |
Vue 使用 change 事件而不是 input 事件来进行同步。这意味着用户必须先修改输入,然后将焦点从输入元素移开,实例属性的值才会更新。(参见示例 7) |
.number |
将输入转换为数字。使用 <input type="number"> 时会自动进行此操作。 |
.trim |
删除输入开头和结尾的空白字符。(参见示例 8) |
custom | 要创建一个自定义的 v-model 修饰符,我们首先需要定义一个名为 'modelModifiers' 的 prop 来存储新的修饰符。修饰符功能写在一个方法中。如果设置了修饰符,则在将值 emit 回父组件之前,会在方法中运行相应的代码。(参见示例 9) |
更多示例
示例 1
使用滑块(<input type="range">
)来更改 'inputValue' 属性值。因为 <input type="text">
元素是通过 v-model
绑定到 'inputValue' 属性的,所以它会自动更新。
<template>
<h1>v-model Example</h1>
<p>Drag the slider to change the 'inputValue' data property, and see the input text field update automatically because of the two-way binding from v-model.</p>
<input type="range" min="-50" max="50" v-on:input="sliderChange" value="4">
<p>inputValue property: "{{ inputValue }}"</p>
<input type="text" v-model="inputValue">
</template>
<script>
export default {
data() {
return {
inputValue: null
};
},
methods: {
sliderChange(evt) {
this.inputValue = evt.target.value
}
}
}
</script>
运行示例 »
示例 2
在组件上使用 v-model
,配合 props
和 emits
,以便 <input>
元素的变化可以更新父级的 'text' 属性。
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>App.vue 'text' property: "{{ text }}"</p>
<comp-one v-model="text"/>
</template>
<script>
export default {
data() {
return {
text: 'Say Cheese'
}
}
}
</script>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Write something in the text input field below to see that changes here are emitted from the component, and the parent 'text' property gets updated by the use of v-model.</p>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue']
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 500px;
}
</style>
运行示例 »
示例 3
在组件上使用 v-model
以更清晰地演示双向绑定。组件可以更新父级的 'text' 属性,并且当父级的 'text' 属性改变时,组件也会更新。
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>App.vue 'text' property: "<pre>{{ text }}</pre>"</p>
<button v-on:click="this.text = 'Hello!'">text='Hello!'</button>
<comp-one v-model="text"/>
</template>
<script>
export default {
data() {
return {
text: 'Say Cheese'
}
}
}
</script>
<style>
pre {
display: inline;
background-color: yellow;
}
</style>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Two-way binding on component with v-model:</p>
<ol>
<li>The component can update the 'text' property (using text field).</li>
<li>The component gets updated when the 'text' property is changed (using button).</li>
</ol>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue']
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 600px;
}
</style>
运行示例 »
示例 4
在组件内部使用 v-model
配合计算值,其中包含 get()
和 set()
函数。
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Two-way binding on component with v-model:</p>
<ol>
<li>The component can update the 'text' property (using text field).</li>
<li>The component gets updated when the 'text' property is changed (using button).</li>
</ol>
<input v-model="inpVal"/>
</div>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
computed: {
inpVal: {
get() {
return this.modelValue;
},
set(inpVal) {
this.$emit('update:modelValue',inpVal)
}
}
}
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 600px;
}
</style>
运行示例 »
示例 5
在组件上使用 v-model:message
将默认 prop 名称 'modelValue' 重命名为 'message'。
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>App.vue 'text' property: "<pre>{{ text }}</pre>"</p>
<button v-on:click="this.text = 'Hello!'">text='Hello!'</button>
<comp-one v-model:message="text"/>
</template>
<script>
export default {
data() {
return {
text: 'Say Cheese'
}
}
}
</script>
<style>
pre {
display: inline;
background-color: yellow;
}
</style>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Two-way binding on component with v-model:</p>
<ol>
<li>The component can update the 'text' property (using text field).</li>
<li>The component gets updated when the 'text' property is changed (using button).</li>
</ol>
<input
:value="message"
@input="$emit('update:message', $event.target.value)"
/>
</div>
</template>
<script>
export default {
props: ['message'],
emits: ['update:message']
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 600px;
}
</style>
运行示例 »
示例 6
在组件上使用两次 v-model
来创建两个值的双向绑定。
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>Name: "<pre>{{ name }}</pre>"</p>
<p>Height: <pre>{{ height }}</pre> cm</p>
<comp-one
v-model:name="name"
v-model:height="height"
/>
</template>
<script>
export default {
data() {
return {
name: 'Olaf',
height: 120
}
}
}
</script>
<style>
pre {
display: inline;
background-color: yellow;
}
</style>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Two inputs are bound to the component with v-model through props and emits.</p>
<p>
<label>
Name:
<input
type="text"
:value="name"
@input="$emit('update:name', $event.target.value)"
/>
</label>
</p>
<p>
<label>
Height:
<input
type="range"
:value="height"
@input="$emit('update:height', $event.target.value)"
min="50"
max="200"
/>
{{ this.$props.height }} cm
</label>
</p>
</div>
</template>
<script>
export default {
props: ['name','height'],
emits: ['update:name','update:height']
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 300px;
}
</style>
运行示例 »
示例 7
使用 .lazy
修饰符,这样用户必须先修改输入元素,然后将焦点移出输入元素,属性才会被 v-model
更新。
<template>
<h1>v-model Example</h1>
<p>Using the '.lazy' modifier, you must first write something, then click somewhere else, or use the tab key to switch focus away from the input element, before the property get updated.</p>
<input type="text" v-model.lazy="inputValue">
<p>inputValue property: "{{ inputValue }}"</p>
</template>
<script>
export default {
data() {
return {
inputValue: null
};
}
}
</script>
运行示例 »
示例 8
使用 .lazy
修饰符,这样用户必须先修改输入元素,然后将焦点移出输入元素,属性才会被 v-model
更新。
<template>
<h1>v-model Example</h1>
<p>Using the '.trim' modifier will remove any white spaces at the start and end of the input.</p>
<p>Add white spaces at the start and end in the input fields below to see the difference with or with out '.trim'.</p>
<p>No '.trim': <input type="text" v-model="inputVal1"> "<pre>{{ inputVal1 }}</pre>"</p>
<p>With '.trim': <input type="text" v-model.trim="inputVal2"> "<pre>{{ inputVal2 }}</pre>"</p>
</template>
<script>
export default {
data() {
return {
inputVal1: 'Hello',
inputVal2: 'Hi'
};
}
}
</script>
<style>
pre {
display: inline;
background-color: lightgreen;
}
</style>
运行示例 »
示例 9
使用自定义的 .allCapital
修饰符,如果在设置了 .allCapital
修饰符的情况下,将输入中的所有字符转换为大写。
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>App.vue 'text' property: "{{ text }}"</p>
<comp-one v-model.allCapital="text"/>
</template>
<script>
export default {
data() {
return {
text: ''
}
}
}
</script>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Write something in the text input field below. Click somewhere else or use the tab key to shift focus away from the input element to see the effect of the custom 'allCapital' modifier.</p>
<input
:value="modelValue"
@change="this.emitVal"
/>
</div>
</template>
<script>
export default {
props: {
modelValue: String,
modelModifiers: {
// modelModifiers is an empty object initially.
// Modifiers set on the component will be stored here.
default: () => ({})
}
},
emits: ['update:modelValue'],
methods: {
emitVal(e) {
let value = e.target.value
if (this.modelModifiers.allCapital) {
value = value.toUpperCase()
}
this.$emit('update:modelValue', value)
}
}
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 500px;
}
</style>
运行示例 »
相关页面
Vue 教程:Vue Components
Vue 教程:Vue Props
Vue 教程:Vue $emit() Method
Vue 教程:Vue Computed Properties
Vue Reference: Vue $emit() Method
Vue Reference: Vue $props Object
JavaScript Tutorial: JavaScript Object Accessors