文章目录
ps:vue3中推荐使用的是reactive写法,文中这种格式只是为了和vue2做对照,只是为了凸显vue3中对v-model的改进,仅供参考。
Vue中的数据绑定
绑定数据有三种方式:
- 插值,也就是{{name}}的形式,以文本的形式和实例data中对应的属性进行绑定
- v-bind
- v-model
v-bind
eg:v-bind:class 可简写为 :class
当加上v-bind:之后,它的值classe不是字符串,而是vue实例对应的data.class的这个变量。也就是说data.class是什么值,它就会给class属性传递什么值,当data.class发生变化的时候,class属性也发生变化,这非常适合用在通过css来实现动画效果的场合。他只是单向变动
v-bind支持的类型
html中的属性、css的样式、对象、数组、number 类型、bool类型
v-bind使用
// 绑定文本
<p v-bind="message"></p>
// 绑定属性
<p v-bind:src="http://...."></p>
<p v-bind:class="http://...."></p>
<p v-bind:style="http://...."></p>
// 绑定表达式
:class{className:true}
v-model
v-model 指令在表单 、 及 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理
主要是用在表单元素中,它实现了双向绑定。在同时使用v-bind和v-model中,v-model建立的双向绑定对输入型元素input, textarea, select等具有优先权,会强制实行双向绑定。很多时候v-model使用在表单的<input>
中实现双向绑定。
v-model实现了表单输入的双向绑定,一般是这么写的:
<div id="app">
<input v-model="price">
</div>
<script>
new Vue({
el: '#app',
data: {
price: ''
}
});
</script>
通过该语句实现price变量与输入值双向绑定
实际上v-model只是一个语法糖,真正的实现是这样的:
<input type="text"
:value="price"
@input="price=$event.target.value">
以上代码分几个步骤:
-
将输入框的值绑定到price变量上,这个是单向绑定,意味着改变price变量的值可以改变input的value,但是改变value不能改变price
-
监听input事件(input输入框都有该事件,当输入内容时自动触发该事件),当输入框输入内容就单向改变price的值
这样就实现了双向绑定。
即
<input v-model="giveDate" />
<input :value="giveDate" @input="giveDate = $event.target.value" />
第一行的代码其实只是第二行的语法糖。
vue2中v-model用法
不使用v-model的双向绑定
如下代码所示,这是不用v-model时候的写法。
父组件使用v-bind给子组件中的props传值。
子组件需要使用emit方法来实现子组件---->父组件
方向上的绑定。
父组件
<!--父组件-->
<template>
<div>
<!--在子组件中用emit("test")传达给父组件的getData方法中-->
<search @test="getData" :keywords="keywords"></search>
<button @click="submit">提交</button>
</div>
</template>
<script>
import search from '@/components/index/search.vue'
export default {
data() {
return {
keywords: ''
}
},
components: {
search
},
methods: {
// 在这里实现更改父组件的值
getData(val){
this.keywords = val
},
submit() {
console.log('keywords:', this.keywords)
}
}
}
</script>
子组件
<!--子组件-->
<template>
<div>
<input @input="inputChange" type="text" name="keywords">
</div>
</template>
<script>
export default {
props: ['keywords'],
methods: {
inputChange(e) {
// 传给父元素的test函数
this.$emit('test', e.target.value)
}
}
}
</script>
即,在父组件中,通过v-bind给子组件传参,同时指定v-on和指定修改目标值的函数。
在子组件中,接受父组件v-bind传过来的值,实现父->子方向上的绑定,然后通过emit方法,向父组件传出$event.target.value,实现子->父方向上的绑定。
使用v-model后
父组件
<!-- parent -->
<template>
<div class="parent"><p>父组件将传递给子组件的数据是:
<input type="text" v-model="giveChildData"/></p>
<Child v-model="giveChildData"></Child>
</div>
</template>
<script>
import Child from './Child.vue';
export default {
name: "Father",
data() {
return {
todosth: 0,
giveChildData: '儿子,爸要把家产传给你啊!'
};
},
components: {
Child
}
}
</script>
子组件
<!-- child -->
<template>
<div class="child">
<p>子组件将通过$emit调用父组件的函数并修改数据:{{ give }}</p>
<span @mousemove="returnBackFn">答复</span>
</div>
</template>
<script>
export default {
name: "Child",
props: {
give:
{
type: String,
default: '0'
}
},
model: {
prop: 'give',
event: 'returnBack'
},
methods: {
returnBackFn() {
this.$emit('returnBack', '啥家产?有矿啊!');
}
}
};</script>
这里的 giveChildData 的值将会传入这个名为 give 的 prop。同时当 <Child>
触发一个 returnBack 事件并附带一个新的值的时候,这个 giveChildData 的属性将会被更新。
问题
但是当有多个数据需要父子组件之间双向绑定时,v-model只能传其中一个值。这时候需要使用.sync来实现其他数据的绑定。
.sync
从 2.3.0 起,vue重新引入了 .sync 修饰符,但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。
示例代码如下:
<comp :foo.sync="bar"></comp>
会被扩展为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:
this.$emit('update:foo', newValue)
实例如下。(弹窗的关闭事件)
<template>
<div class="details">
<myComponent
:show.sync='valueChild'
style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"
>
</myComponent>
<button @click="changeValue">toggle</button>
</div>
</template>
<script>
import Vue from 'vue'
Vue.component(
'myComponent', {
template: `
<div v-if="show">
<p>默认初始值是{{ show }},所以是显示的</p>
<button @click.stop="closeDiv">关闭</button>
</div>`,
props: ['show'],
methods: {
closeDiv() {
this.$emit('update:show', false); //触发 input 事件,并传入新值
}
}
})
export default {
data() {
return {
valueChild: true,
}
},
methods: {
changeValue() {
this.valueChild = !this.valueChild
}
}
}
</script>
效果如下图。
vue2中使用v-model + .sync
由于v-model只能默认传进去一个值,剩下的需要使用.sync实现双向绑定。
对于v-model传进来的数,子组件用emit方法向父组件的“input”传值(注:是v-model默认监听的方法)。
而对于.sync传进来的数,则是通过在子组件中,使用emit向父组件的update:绑定数据名,进行传值(注:默认提供了update方法,当然也可以自己在父组件中自定义一个修改绑定值的函数)。
父组件
<h1>name01:{{ name01 }} </h1>
<h1>age01:{{ age01 }} </h1>
<model01
:model="age01"
:name.sync="name01"
>
</model01>
...
<script>
export default{
data(){
return{
age01: 18,
name01: "username"
}
}
}
</script>
子组件
<template>
<div class="custom-input">
<h1>vue2中的v-model</h1>
<input
type="text"
:value="age"
@input="$emit('input', $event.target.value)"
/>
<!--上面的emit中的“input”是v-model默认监听的函数-->
<br>
<input type="text" :value="name" @input="$emit('update:name', $event.target.value)"/>
<!--name使用的是.sync实现绑定的-->
</div>
</template>
<script>
export default {
name: "Model01",
props: [
'age',
'name'
],
model:{
}
methods: {
}
}
</script>
<style scoped>
</style>
vue3中v-model的使用
接下来就是正点了。
父组件
vue3中,将之前的v-model和.sync整合到一起了,并淘汰了.sync写法。
现在,v-model在组件之间的使用再也不用像以前那样臃肿了,极其舒爽。
只需要在v-model上指定传值。例如:
<model02
v-model:age="age02"
v-model:name="name02"
></model02>
这里就是将父组件中的age02变量传入到子模块的props中的age变量中。
子组件只要使用如下调用update:age的方式,就能将age的变化由子组件的age传入到父组件的age02变量上。
$emit('update:age', 传给父组件的值);
....
<h1>name02:{{ name02 }} </h1>
<h1>age02:{{ age02 }} </h1>
<!-- vue3写法-->
<model02
v-model:age="age02"
v-model:name="name02"
></model02>
....
子组件
<template>
<div class="custom-input">
<h1>vue3中的v-model</h1>
<input type="text" :value="age" @input="onAgeInput"/>
<br>
<input type="text" :value="name" @input="onNameInput"/>
</div>
</template>
<script>
export default {
name: "Model02",
props: [
'age',
'name'
],
methods: {
onAgeInput(e) {
this.$emit('update:age', parseFloat(e.target.value));
},
onNameInput(e) {
this.$emit('update:name', e.target.value)
}
}
}
</script>
<style scoped>
</style>
实现效果图:
总结
vue3中的v-model比以前舒爽太多了!
ps:vue3中推荐使用的是setUp()写法,上面这种格式只是为了和vue2做对照,只是为了凸显vue3中对v-model的改进,仅供参考。