由于 vue 是单向数据流,在父子组件传值过程中,我们只可以使用 props 将父组件值传递给子组件,而不可以直接在子组件修改 props 传递的数据;如果在子组件改变 props 传递过来的值,控制台会报错。
也就是说,数据总是从父组件传到子组件,子组件无法修改父组件传递过来的值,想要修改父组件传递过来的值,只能通过父组件对原始数据进行修改。
根据上面的思路我们可以实现组件传值的双向绑定,总结了如下几种方法:
一、常规方法
父组件通过 props 传值给子组件,同时提供一个方法更新自己的数据;
子组件接收数据,然后在子组件里修改数据时触发父组件的方法将最新值传递给父组件。
// 父组件
<template>
<child :msg='msg' :changeMsg='changeMsg'></child>
</template>
<script>
import child from './child.vue';
export default {
components:{
child };
data(){
return {
msg:0
}
},
methods:{
changeMsg(data){
this.msg = data;
}
}
}
</script>
// 子组件
<template>
<div>
<el-input :value='msg' @input='change($event)'></el-input>
</div>
</template>
<script>
export default {
name:'child',
props:{
msg:[Number,String],
changeMsg:Function //参数形式传递方法
},
methods:{
change(e){
this.changeMsg(e);
}
}
}
</script>
还有一种类似的方法:父组件通过 props 传值给子组件并提供一个更新自身数据的方法,子组件在修改值的时候通过 $emit 触发父组件方法并将最新值传递过去。
二、.sync 修饰符
父组件使用 .sync 双向绑定,子组件通过 $emit 触发 update:prop 名来修改父组件的数据,以实现双向绑定。
// 父组件
<template>
<child :msg.sync='msg'></child>
</template>
<script>
import child from './child.vue';
export default {
components:{
child };
data(){
return {
msg:0
}
},
methods:{
changeMsg(data){
this.msg = data;
}
}
}
</script>
// 子组件
<template>
<div>
<el-input :value='msg' @input='change($event)'></el-input>
</div>
</template>
<script>
export default {
name:'child',
props:{
msg:[Number,String]
},
methods:{
change(e){
// update为固定字段,通过冒号连接msg,将e传递给父组件的变量
this.$emit('update:msg',e);
}
}
}
</script>
.sync 修饰符是一个简写:
<child :msg.sync='msg'></child>
//相当于
<child :msg.sync='msg' @update:msg='msg=e'></child>
三、v-model
vue 自定义组件v-model的语法糖:
<child v-model='msg'></child>
//相当于
<child :msg='msg' @input='val => { msg = val }'></child>
所以我们在子组件修改父组件值只需要调用 input 方法就可以了,这里的 input 是默认的方法。
// 父组件
<template>
<child :msg='msg' v-model='msg'></child>
</template>
<script>
import child from './child.vue';
export default {
components:{
child };
data(){
return {
msg:0
}
}
}
</script>
// 子组件
<template>
<div>
<el-input :value='msg' @input='change($event)'></el-input>
</div>
</template>
<script>
export default {
name:'child',
props:{
msg:[Number,String]
},
methods:{
change(e){
this.$emit('input',e);
}
}
}
</script>
如果不想使用 input,想使用自定义方法来改变父组件的值只需要在父组件中定义一个方法,在子组件配置一下model就可以了
// 子组件
<template>
<div>
<el-input :value='msg' @input='change($event)'></el-input>
</div>
</template>
<script>
export default {
name:'child',
model:{
prop:'msg', //这是父组件传递的值
event:'changeData' //父组件定义的更新自身数据的方法
},
props:{
msg:[Number,String]
},
methods:{
change(e){
this.$emit('changeData',e);
}
}
}
</script>