Vue组件之单向数据流

什么是单向数据流?

prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。

另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。如果你这么做了,Vue 会在控制台给出警告。

这是官网给的说明,说白了就是父组件通过props传递给子组件的数据不能在子组件里修改,否则浏览器会报错。

实例

先来看看如果不听话的结果:

1
2
3
<div id="app">
<my-input :val="1"></my-input>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>

Vue.component('my-input',{
props:['val'],
template:'<input type="button" :value="val" @click="changeVal">',

methods:{
changeVal:function(){
this.val++;
}
}
});

new Vue({
el:'#app'
});
</script>

上面代码中,父组件传递给子组件一个val数值,绑定在value上,并给按钮添加一个点击事件,点击时val做加一运算。在浏览器中运行,发现效果可以实现,即val值增加了,咋一眼会觉得单向数据流扯淡( • ̀ω•́ )✧。 当然,这是天真的想法。打开控制台,你会发现一行大大的红字:

[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: “val”

显然,不听话是会吃亏的。浏览器报错了,并友情地提示了怎么解决,你看到了吗?

解决办法

  1. 如果你想把它当作局部数据来用,那么就定义一个局部变量,并用 prop 的值初始化它;

  2. 如果你想将它处理成其它数据输出,那么就定义一个计算属性,处理 prop 的值并返回。

首先看第一种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Vue.component('my-input',{
props:['val'],

template:'<input type="button" :value="counter" @click="changeVal">',

data:function(){
return {counter:this.val}
},

methods:{
changeVal:function(){
this.counter++;
}
}
});

上面代码中,将传入的val赋给counter,然后直接操作this.counter,这时浏览器就不会报错了,注意data的书写格式,必须是函数。

第二种方法:

1
2
3
4
5
6
7
8
9
10
Vue.component('my-input',{
props:['val'],
template:'<input type="button" :value="changeVal">',
computed:{
changeVal:function(){
return this.val+100;
}
}

});

这时,我们希望将传过来的值加100,得到新的数值,通过计算属性轻松实现。