上篇学习了如何把父组件的数据传递给子组件,尽管子组件内部不能改变prop的值,但子组件能把自己的数据传递给父组件。
我们通过自定义事件来实现。
#事件绑定
$on(eventName) 监听事件
$emit(eventName) 触发事件
父组件可以在使用子组件的地方直接用v-on来监听子组件触发的事件
[注意]不能用$on侦听子组件抛出的事件,直接在模板里用v-on绑定
HTML
<div id="example"> <parent></parent> </div>
Js
var childNode = { template: `<button @click="incrementCounter">{{ counter }}</button>`, data() { return { counter: 0 } }, methods: { incrementCounter() { this.counter++; this.$emit('increment'); // 子组件内用$emit触发事件 } }, }; var parentNode = { // 父组件内通过v-on(简写@)监听事件 template: ` <div class="parent"> <p>{{total}}</p> <child @increment="incrementTotal"></child> <child @increment="incrementTotal"></child> </div> `, components: { 'child': childNode //声明子组件 }, data() { return { 'total': 0 } }, methods: { incrementTotal() { this.total++; } } }; // 创建根实例 new Vue({ el: '#example', components: { 'parent': parentNode //声明父组件 } })
上面的代码中,子组件button一旦发生click事件,就会执行incrementCounter方法,而incrementCounter()会通过$emit触发increment事件,而在父组件内,通过v-on(简写@)监听increment事件,一旦increment被触发,则执行incrementTotal方法。
最终实现的效果如图
左边点击一次右边点击两次:
#数据传递
子组件通过$emit触发事件,$emit方法的第一个参数为要触发的事件,第二个参数为要传递的数据。
var childNode = { template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="childMsg" @input="data"> </div> <p>{{childMsg}}</p> </div> `, data() { return { childMsg: '' } }, methods: { data() { this.$emit('pass-data', this.childMsg) // pass-data为被触发的事件,this.childMsg为要传递给父组件的数据 } } } var parentNode = { // v-on监听pass-data是否被触发,如果触发了,就执行getData方法 template: ` <div class="parent"> <div> <span>父组件数据</span> <input v-model="msg"> </div> <p>{{msg}}</p> <child @pass-data="getData"></child> </div> `, components: { 'child': childNode }, data() { return { 'msg': 'match' } }, methods: { getData(value) { // getData方法接收的参数value的值就是从子组件接收的数据this.childMsg this.msg = value; } } }; // 创建根实例 new Vue({ el: '#example', components: { 'parent': parentNode } })
最终效果如下
修改子组件中input值,父组件则接收到相同的值并显示出来