最近在学习Vue,昨天的一个小项目中我遇到了一个需求:首页点击注册,注册为弹框组件,决定它显隐的是父组件data里的registerVisible值,我尝试在注册这个组件里点击提交按钮后,改变registerVisible的值为false,发现报错:

1
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "result" (found in component )

通过搜索了解到:

以前在Vue1.x中利用propstwoWay.sync绑定修饰符就可以实现props的双向绑定功能,但是在Vue2中抛弃了此功能,组件内不能修改props的值,同时修改的值也不会同步到组件外层,即调用组件方不知道组件内部当前的状态是什么,子组件修改父组件传过来的prop值的话就会报以上错误。


所以props双向绑定需要自己来实现,这里我用一个checkbox选择按钮的选取状态来举例:

需求:

  1. 点击checkbox 选择/取消 状态切换
  2. 不点击checkbox 也可通过子组件按钮来切换选取状态
    步骤如下

1.在组件内的data对象中创建一个props属性的副本

1
2
3
4
5
6
7
8
9
10
11
12
Vue.component("togglebtn", {
template: "<button @click='change'>{{myChoose?'取消':'选择'}}</button>",
data: function () {
return {
myChoose: this.choose //创建props属性choose的副本--myChoose
};
},
props: [
"choose"
],
......
});

2.创建针对props属性的watch来同步组件外对props的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Vue.component("togglebtn", {
template: "<button @click='change'>{{myChoose?'取消':'选择'}}</button>",
data: function () {
return {
//创建props属性choose的副本--myChoose
myChoose: this.choose
};
},
props: [
"choose"
],
watch: {
choose(val) {
//监听外部对props属性choose的变更,并同步到组件内的data属性myChoose中
this.myChoose = val;
},
......
}

3.创建针对props副本的watch,通知到组件外

1
2
3
4
5
6
<div id="app">
<!--开关组件-->
&lt;input id="checkbox" type="checkbox" checked="checked" @click="change"&gt;&lt;br/&gt;
<!--外部控制-->
&lt;togglebtn :choose="choose" @on-choose-change="onChooseChange" /&gt;
&lt;/div&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Vue.component("togglebtn", {
template: "&lt;button @click='change'&gt;{{myChoose?'取消':'选择'}}&lt;/button&gt;",
data: function () {
return {
//创建props属性choose的副本——myChoose
myChoose: this.choose
};
},
props: [
"choose"
],
watch: {
choose(val) {
//监听外部对props属性choose的变更,并同步到组件内的data属性myChoose中
this.myChoose = val;
},
myChoose(val){
//组件内对myChoose变更后向外部发送事件通知
this.$emit("on-choose-change",val);
}
},
methods: {
change() {
this.myChoose = !this.myChoose;
}
}
});

完整代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">

<input id="checkbox" type="checkbox" checked="checked" @click="change"><br/>

<togglebtn :choose="choose" @on-choose-change="onChooseChange" />
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>
<script>
// 组件
Vue.component("togglebtn", {
template: "<button @click='change'>{{myChoose?'取消':'选择'}}</button>",
data: function () {
return {
//创建props属性choose的副本--myChoose
myChoose: this.choose
};
},
props: [
"choose"
],
watch: {
choose(val) {
//监听外部对props属性choose的变更,并同步到组件内的data属性myChoose中
this.myChoose = val;
// 这里对checked状态做了处理
document.getElementById('checkbox').checked = val
},
myChoose(val){
//组件内对myChoose变更后向外部发送事件通知
this.$emit("on-choose-change",val);
}
},
methods: {
change() {
this.myChoose = !this.myChoose;
}
}
});

new Vue({
el: "#app",
data: function() {
return {
choose: true
}
},
methods: {
change() {
this.choose = !this.choose;
},
onChooseChange(val){
//外层调用组件方注册变更方法,将组件内的数据变更,同步到组件外的数据状态中
this.choose=val;
}
}
});
</script>
</body>
</html>