Vue组件化开发(二)——组件间的通信
前言
使用Vue进行前端项目开发,项目中必然会使用和封装各种组件来保障代码的可复用性,但组件化开发也带来了一个问题,就是组件间的数据通信,本篇文章将围绕组件通信的问题,根据组件间的关系(父子组件以及非父子组件)来介绍常用的通信方式。
一、父子组件的通信
(一)父组件传递数据给子组件
父组件给子组件传递数据时,子组件可以使用props
关键字来接收父组件的数据,定义props的值有两种方式,分别是:
- 方式1:字符串数组,数组中的字符串就是传递时的名称。
- 方式2:对象,对象可以设置传递时的类型,也可以设置默认值等。
我们用的比较多的常常是第二种,因为后者可以对props参数进行约束,避免父组件传过来不符合要求的数据。下面我们就先用方式1来做一个简单的用例吧。
<div id="app">
<div>
<h3>我是父组件</h3>
<child-component :cmessage="message"></child-component>
</div>
</div>
<template id="child">
<div>
<h2>我是子组件,我接收到父组件的值是:</h2>
<h4>{{cmessage}}</h4>
</div>
</template>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message : 'TODAY IS SUNDAY'
},
components:{
'childComponent': {
template: '#child',
props: ['cmessage']
}
}
})
</script>
(二)子组件进行props的数据验证
除了数组之外,我们也可以使用对象,当需要对props
进行类型等验证时,就需要对象写法了,Vue支持我们对String、Number等类型的数据验证之外,还支持默认值设置、字段是否为必填字段、自定义校验方式等功能。
<div id="app">
<div>
<h3>我是父组件</h3>
<child-component
:data1="data1"
:data2="data2"
:data3="data3"
:data6="data6"
/>
</div>
</div>
<template id="child">
<div>
<h2>我是子组件,我接收到父组件的值是:</h2>
<h5>{{data1}}</h5>
<h5>{{data2}}</h5>
<h5>{{data3}}</h5>
<h5>{{data4}}</h5>
<h5>{{data5}}</h5>
<h5>{{data6}}</h5>
</div>
</template>
<script src="./vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
data1: 100,
data2: '200',
data3: '200',
data6: '12345'
},
components:{
'childComponent': {
template: '#child',
props:{
// 单独定义接收参数的数据类型
data1: Number,
// 定义参数可接受的多种数据类型
data2: [String,Number],
// 定义必填字段
data3: {
type: String,
required: true
},
// 定义带有默认值的对象
data4: {
type: Object,
default: function(){
return {name: 'xiaoming',age: 19}
}
},
// 定义带有默认值的数据
data5: {
type: Number,
default: 200
},
// 自定义验证函数
data6: {
validator: function(value){
return value.length > 5
}
}
}
}
}
})
</script>
我们可以看到,如果说存在porps参数不符合要求的情况,控制台会进行报错提醒。
(三)子组件传递数据给父组件
子组件传递数据给父组件时,可以通过自定义事件来进行实现,自定义事件的实现流程如下:
- 在子组件中,通过$emit()来触发事件。
- 在父组件中,通过v-on来监听子组件事件,一般用@事件名来进行简写
具体的实现我们可以参考下面的这个例子:
<div id="app">
<div>
<!-- 这里需要注意,@plus相当于是使用了 v-on:plus=""来进行自定义事件的绑定 -->
<child-component @plus="changeCount" @minus="changeCount"></child-component>
<h3>我是父组件,子组件当前的count值为:</h3>
<h5>{{pCount}}</h5>
</div>
</div>
<template id="child">
<div>
<h2>我是子组件:</h2>
<div>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
</div>
</div>
</template>
<script src="./vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data(){
return {pCount: 0}
},
methods: {
changeCount(val){
this.pCount = val;
}
},
components:{
'childComponent': {
template: '#child',
data(){
return {
count: 0
}
},
methods:{
increment(){
this.count = this.count + 1;
this.$emit('plus',this.count)
},
decrement(){
this.count = this.count - 1;
this.$emit('minus',this.count);
}
}
}
}
})
</script>
![](http://upload-images.jianshu.io/upload_images/24009055-1252de0a55e9bf03.png?imageMogr2/auto-orient/strip|imageView2/2/w/774/format/webp)
二、父子组件的访问方式
(一)父子组件的访问方式 $children
有时候我们需要父组件直接访问子组件,这里的访问指的是直接获取到子组件的实例对象,我们此时就可以通过this.$children
来进行获取,this.$children
可以让我们获取当前组件下的所有子组件集合。
<div id="app">
<div>
<!-- 这里需要注意,@plus相当于是使用了 v-on:plus=""来进行自定义事件的绑定 -->
<button @click="showChild">ShowChild</button>
<child-component></child-component>
</div>
</div>
<template id="child">
<div>
<h2>我是子组件:</h2>
<div>
<grand-child1></grand-child1>
<grand-child2></grand-child1>
</div>
</div>
</template>
<template id="child1">
<div>
<h4>我是孙组件1</h4>
</div>
</template>
<template id="child2">
<div>
<h4>我是孙组件1</h4>
</div>
</template>
<script src="./vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data(){
return {pCount: 0}
},
methods: {
showChild(){
for(let i = 0; i< this.$children.length ; i++){
console.log(this.$children[i]);
}
}
},
components:{
'childComponent': {
template: '#child',
data(){
return {
count:0
}
},
components:{
'grand-child1':{
template: '#child1'
},
'grand-child2':{
template: '#child2'
}
}
}
}
})
</script>
(二)父子组件的访问方式: $refs
通过$children
访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs
。
<div id="app">
<div>
<!-- 这里需要注意,@plus相当于是使用了 v-on:plus=""来进行自定义事件的绑定 -->
<button @click="showChild">ShowChild</button>
<child-component ref="child"></child-component>
</div>
</div>
<template id="child">
<div>
<h2>我是子组件:</h2>
<div>
<grand-child1></grand-child1>
<grand-child2></grand-child1>
</div>
</div>
</template>
<template id="child1">
<div>
<h4>我是孙组件1</h4>
</div>
</template>
<template id="child2">
<div>
<h4>我是孙组件1</h4>
</div>
</template>
<script src="./vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data(){
return {pCount: 0}
},
methods: {
showChild(){
console.log(this.$refs.child.count);
}
},
components:{
'childComponent': {
template: '#child',
data(){
return {
count: 100
}
},
components:{
'grand-child1':{
template: '#child1'
},
'grand-child2':{
template: '#child2'
}
}
}
}
})
</script>
(三)父子组件的访问方式: $parent
<div id="app">
<div>
<!-- 这里需要注意,@plus相当于是使用了 v-on:plus=""来进行自定义事件的绑定 -->
<child-component></child-component>
</div>
</div>
<template id="child">
<div>
<h2>我是子组件:</h2>
<div>
<button @click="showParent">showParent</button>
</div>
</div>
</template>
<script src="./vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data() {
return { pCount: 0 }
},
methods: {
},
components: {
'childComponent': {
template: '#child',
data() {
return {
}
},
methods: {
showParent() {
console.log(this.$parent)
}
}
},
}
})
</script>
如果我们想在子组件中直接访问父组件,可以通过$parent
来进行获取,
注意事项:
(1)尽管在Vue开发中,我们允许通过$parent
来访问父组件,但是在真实开发中尽量不要这样做。子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
(2) 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
(3) 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护
三、非父子组件的通信
非父子组件关系包括多个层级的组件,也包括兄弟组件的关系。
在Vue1.x的时候,可以通过$dispatch
和$broadcast
完成
- $dispatch用于向上级派发事件
- $broadcast用于向下级广播事件
但是在Vue2.x都被取消了,在Vue2.x中,有一种方案是通过中央事件总线,也就是一个中介来完成。但更好的方式是使用Vuex
来进行全局的状态管理,关于Vuex的使用,可以参考一下我的这篇文章:Vuex的使用