组件关系分为父子组件通信、兄弟组件通信和跨级组件通信
自定义事件–子组件给父组件传递数据
使用 v-on 除了监听 DOM 事件外,还可以用于组件之间的自定义事件
JS 的设计模式–观察者模式,有dispatchEvent 和 addEventListener 两个方法,Vue 组件也有与之类似的一套模式,子组件用 $emit
来触发事件,父组件用 $on()
来监听子组件事件。
步骤:
- 自定义事件;
- 在子组件用
$emit
触发事件,第一个参数是事件名,后面的参数是要传递的数据; - 在自定义事件中用一个参数来接收
<div id="app">
count 的值为{{count}}
<hr>
<my-component @change="handle"></my-component> // 自定义 change 事件
</div>
<script>
var app = new Vue({
el: '#app',
data: { count: 1000 },
methods: {
handle: function(value) {
this.count = value // 接收
}
},
components: {
'my-component': {
template: `<div><button @click="plus">+1000</button><button @click="cut">-1000</button></div>`,
data: function() {
return {
count: 1000
}
},
methods: {
plus: function() {
this.count += 1000
this.$emit('change', this.count) // 触发事件并传递数据
},
cut: function() {
this.count -= 1000
this.$emit('change', this.count)
}
}
}
})
</script>
组件使用 v-model
v-model 其实是一个语法糖,其在背后做了两件事:
- 用 v-bind 绑定一个 value 属性
- 用 v-on 指令给当前元素绑定一个 input 事件
使用 v-model 时,v-model 做的是
- 接收一个 value 属性
- 当有新的 value 时触发 input 事件
<div id="app">
count:{{count}}
<hr/>
<child-component v-model="count"></child-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: { count: 0 },
components: {
'child-component': {
data: function() {
return { count: 0 }
},
template: '<button @click="plus">+1</button>',
methods: {
plus: function() {
this.count++
this.$emit('input', this.count) // 触发 input 事件
}
}
}
}
})
</script>
this.$emit
这行代码其实就是触发了 input 事件,而 input 事件后面的参数就是传递给 v-model 绑定的 value 属性的值。
v-model 其实就做到了绑定事件加上接收事件触发传递过来的参数数据。
非父子组件之间的通信
我们有时候也需要非父子关系组件之间的通信,简单场景下可以使用一个空的 Vue 实例作为中央事件总线:
var bus = new Vue()
接收消息数据的组件
bus.$on('msg', function(msg) {
// 监听事件并接收传递过来的参数,参数可以是多个
console.log(msg)
...
})
发送消息数据的组件
bus.$emit('msg', "这是来自发送组件的消息")
具体示例:
<div id="app">
<a-component></a-component>
<b-component></b-component>
</div>
---------------------------
| 根组件 |
| ----------- |
| | 子组件 a | |
| ----------- |
| |
| ----------- |
| | 子组件 b | |
| ----------- |
| |
---------------------------
Vue.component('a-component', {
template: '<button @click="setmsg">组件a</button>',
methods: {
setmsg: function() {
this.$root.bus.$emit('msg', "这是来自a组件的消息")
}
}
})
Vue.component('b-component', {
template: '<div></div>',
created: function() {
this.$root.bus.$on('msg', function(msg) {
console.log(msg)
})
}
})
var app = new Vue({
el: '#app',
data: {
bus: new Vue()
}
})
父链:this.$parent
可以通过 this.$parent.xxx
来修改父组件的指定内容
<div id="app">
<child-component></child-component>
<br/>
这是父组件的 msg:{{msg}}
</div>
Vue.component('child-component', {
template: '<button @click="setFatherData">改变父组件数据</button>',
methods: {
setFatherData: function() {
this.$parent.msg = "通过子组件修改的数据"
}
}
})
var app = new Vue({
el: '#app',
data: {
msg: '父组件的初始数据'
}
})
子链:this.$refs
为子组件添加特殊属性 ref 作为索引
通过 this.$refs.索引.xxx
来获取指定子组件的内容
<div id="app">
<a-component ref="a"></a-component>
<b-component ref="b"></b-component> // 添加索引
<br/>
<button @click="getmsg">获取子组件 a 的 msg </button>
<br/>
父组件当前的 msg:{{msg}}
</div>
Vue.component('a-component', {
template: '<div></div>',
data: function() {
return {
msg: '组件 a msg'
}
}
})
Vue.component('b-component', {
template: '<div></div>',
data: function() {
return {
msg: '组件 b msg'
}
}
})
var app = new Vue({
el: '#app',
data: {
msg: '父组件初始 msg'
},
methods: {
getmsg: function() {
this.msg = this.$refs.a.msg // 获取指定子组件内容
}
}
})