前言
我们在vue项目中,经常有这样的需求,父组件绑定v-model,子组件输入更改父组件v-model绑定的数值。很多朋友对这种操作不是很清楚,这需要对v-model有比较深入的了解,今天谈谈v-model。
vue的双向数据绑定
这个问题在很多前端面试中,会提及。面试官问你,vue的双向数据绑定原理是什么?当然,这个也是耳熟能详的问题,百度随便一搜索,到处都能找到答案。
一般都会搜索到,是用Object.defineProperty( ),来监听数据get和set,来实现数据劫持的。
假如对Object.defineProperty( )不是很了解,可以点击上面链接,看看其语法!
举个简单的例子:
var blog = {
name: 'haorooms博客'
};
console.log(blog.name); // haorooms博客
如果想要在执行console.log(blog.name)的同时,直接给haorooms博客加个书名号,那要怎么处理呢?或者说要通过什么监听对象 blog的属性值。这时候Object.defineProperty( )就派上用场了,代码如下:
var blog= {}
var name = '';
Object.defineProperty(blog, 'name', {
set: function (value) {
name = value;
console.log('欢迎来到' + value);
},
get: function () {
return '《' + name + '》'
}
})
blog.name = 'haorooms博客'; // 欢迎来到haorooms博客
console.log(blog.name); // 《haorooms博客》
上面代码执行一下,可以查看效果!
关于 vue的双向数据绑定,具体如何实现的?网上也有很多实现的代码,这里就不多累赘了!
v-model 的使用
上面开了一个小差,讲了那么多,其实就是为了说明Vue 是单项数据流,v-model 只是语法糖而已。
<input v-model="haorooms" />
<input v-bind:value="haorooms" v-on:input="haorooms= $event.target.value" />
第一行的代码其实只是第二行的语法糖。然后第二行代码还能简写成这样:
<input :value="haorooms" @input="haorooms= $event.target.value" />
也就是说,
<input v-model="haorooms" />
也可以写成
<input :value="haorooms" @input="haorooms= $event.target.value" />
v-model在组件上的使用
回到我们一开始的疑问了?vue中v-model如何进行父子组件的通信?如何在组件中使用呢?假如你理解了我上面的例子,那么,我们就可以很简单的在组件中使用v-model了。
父组件代码如下:
<template>
<div id="demo">
<test-model v-model="haorooms"></test-model>
<span>{{haorooms}}</span>
</div>
</template>
<script>
import testModel from 'src/components/testModel'
export default {
data(){
return{
haorooms: "haorooms"
}
},
components: {
testModel,
}
}
</script>
<style lang="scss" scoped>
</style>
子组件代码如下:
<template>
<span>
<input
ref="input"
:value="value"
@input="$emit('balabala', $event.target.value)"
>
</span>
</template>
<script>
export default {
data(){
return{
}
},
props: ["value"],
model: {
prop: 'value',
event: 'balabala'
}
}
</script>
<style lang="scss" scoped>
</style>
我们可以通过上面代码,子组件修改父组件v-model绑定的值!
实现截图如下:
v-model 的缺点和解决办法
在创建类似复选框或者单选框的常见组件时,v-model就不好用了。
<input type="checkbox" v-model="haorooms" />
我们可以用如下方式解决:
<input type="checkbox" :checked="status" @change="status = $event.target.checked" />
父组件可以如下写:
<my-checkbox v-model="haorooms"></my-checkbox>
子组件:
<input
type="checkbox"
<!--这里就不用 input 了,而是 balabala-->
@change="$emit('balabala', $event.target.checked)"
:checked="value"
/>
export default {
data(){
return{
}
},
props: ['checked'], //这里就不用 value 了,而是 checked
model: {
prop: 'checked',
event: 'balabala'
},
}
render 函数中的v-model
render 函数中没有与 v-model 相应的 api,根据上面的原理,可以自己实现!
render: function (createElement) {
var self = this
return createElement('input', {
domProps: {
value: self.value
},
on: {
input: function (event) {
self.value = event.target.value
self.$emit('input', event.target.value)
}
}
})
}
同步修饰符sync
为了保持单向数据流,vue2.0的sync已经成了阉割版,文档如下:如下代码
<comp :foo.sync="bar"></comp>
会被扩展为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:
this.$emit('update:foo', newValue)
和上面v-model一样,.sync修饰符其实也就是一个语法糖。
关于vue的v-model等父子组件通信就写到这里。欢迎留言交流!