在Vue.js的组件中,prop是“单向绑定”的,数据只能从父组件传输到子组件。Vue文档中的说了这样做的原因:
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
用自定义事件向父组件发送数据
但是有时候的确需要由子组件从父组件获取更新的数据,这时候一个相对简单的方法就是——自定义事件。父组件监听事件,在事件的回调函数中得到数据。子组件emit事件,同时发送数据。
父组件
<template><div id="app"><HelloWorld @click="clickData" /><div> {{abc}} </div></div>
</template><script>
import HelloWorld from "./components/HelloWorld.vue";
export default {name: "App",components: {HelloWorld,},data() {return {abc: 123}},methods: {clickData (value) {this.abc = value}},
};
</script>
子组件
<template><div class="hello"><button @click="clickadd">按下</button></div>
</template><script>
export default {name: 'HelloWorld',methods: {clickadd () {this.$emit('click', '456')}}
}
</script>
按下按钮前:
按下按钮后:
123和456表示的是父组件中变量abc的值,子组件按下按钮时,调用了this.$emit,第一个参数表示时间名,第二个参数就可以传递想要发送到父组件的数据了。
父组件这边则在子组件的html标签上监听同名的事件(@click="clickData"),编写回调函数接收值并赋值给变量abc。
用自定义事件实现子组件prop双向绑定
更进一步,如果我们想改变传输到子组件的prop所绑定的父组件的变量,我们可以这样写:
父组件
<template><div id="app"><HelloWorld :msg="abc" @update:msg="clickprop"/><div> {{ abc }} </div></div>
</template><script>
import HelloWorld from "./components/HelloWorld.vue";
export default {name: "App",components: {HelloWorld,},data() {return {abc: "123",};},methods: {clickprop(value) {this.abc = value}},
};
</script>
子组件
<template><div class="hello"><div id="demo" @click="clickadd">{{ msg }}</div></div>
</template><script>
export default {name: 'HelloWorld',inhertAttrs: true,props: {msg: {type: String,}},data: function() {return {}},methods: {clickadd () {this.$emit('update:msg', '456')}}
}
</script>
这样子组件和父组件的值会同时变化。按下上面的123之前:
按下上面的123之后:
值456首先通过emit传递到了父组件,再被赋值给了父组件变量abc,再通过子组件绑定的prop传递回父组件,最终同时变化成功。这样也算实现了“双向绑定”。
使用.sync修饰符简化双向绑定代码
代码中的事件名“update:msg”仅仅是一个名字而已,不和prop名相同也可以。但是如果我们保持与prop名相同,Vue为上面的代码提供了一个简写的形式:使用.sync操作符。使用它,事件名固定为了“update:prop名”的形式,我们也不需要自己写回调函数和绑定事件的代码了。
父组件
<template><div id="app"><HelloWorld :msg.sync="abc" /><div> {{ abc }} </div></div>
</template><script>
import HelloWorld from "./components/HelloWorld.vue";
export default {name: "App",components: {HelloWorld,},data() {return {abc: "123",};},methods: {},
};
</script>
子组件
<template><div class="hello"><div id="demo" @click="clickadd">{{ msg }}</div></div>
</template><script>
export default {name: 'HelloWorld',inhertAttrs: true,props: {msg: {type: String,}},data: function() {return {}},methods: {clickadd () {this.$emit('update:msg', '456')}}
}
</script>
这段代码与上一部分实现相同的效果。
可以看到,.sync修饰符仅仅起到一个简化作用而已。