组件是vue.js
最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。而组件间的通信成为必须解决的话题。vue中组件通信的方式有很多:props/$emit、vuex、eventBus(总线)、provide/inject、$parent/$children/ref、$attrs/$listeners
等
props/$emit 适合父子组件通信
子组件获取父组件data
可以在父组件上使用自定义属性绑定(传递)数据(动态的数据需使用 :m='msg',静态的不需要加),在子组件中需要显式的用props声明自定义属性名(接收数据)
1
2
3
4
5
6
7
8
9
10
11
12// <father :msg="动态数据" /> 传递的是变量
// <father msg="静态数据" /> 传递的是字符串
// 父组件 list: [1,2]
<child :msg="list" test="list" />
// 子组件接收
export default {
props: ['msg', 'test'],
mounted () {
console.log(this.msg) // [1, 2]
console.log(this.test) // list
}
}父组件获取子组件数据/子组件向父组件传值
需要用到自定义事件,父组件用$on监听自定义事件,子组件使用$emit触发父组件关联的自定义事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<template>
<child @childMsg="getChildData" />
</template>
<script>
new Vue({
el: '#app',
components: {
"child": {
data() {
return { msg: '子组件数据'}
},
template:`<button @click="send" >发送</button>`,
methods: {
send () {
this.$emit('childMsg', this.msg)
}
}
}
},
methods: {
getChildData(arg){
console.log(arg) // 子组件数据
}
}
})
</script>
eventBus(总线/发布订阅模式/观察者模式) vue本身支持的自定义事件 $on和$emit
通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,轻量地实现了任何组件间的通信,包括父子、兄弟、跨级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37<!-- 子组件 -->
<button @click="addTitle">add</button>
<script>
import eventBus from './eventBus'
export default {
data() { return { title: 'tew' } },
methods: {
addTitle() {
// 调用自定义事件
eventBus.$emit('onAddTitle', this.title)
}
}
}
</script>
<!-- eventBus.js -->
<!-- 创建一个事件总线并将其导出,以便其他模块可以使用或者监听它 -->
import Vue from 'vue'
export default new Vue()
<!-- 其他组件 -->
<script>
import eventBus from './eventBus'
export default {
methods: {
addTitleHandler(title) {
console.log('on add title', title)
}
},
mounted() {
// 绑定自定义事件
eventBus.$on('onAddTitle', this.addTitleHandler)
},
beforeDestroy() {
// 及时销毁,否则可能造成内存泄露
eventBus.$off('onAddTitle')
}
}
</script>
Vuex
vuex是vue用来管理数据状态的一种机制,将Vue中所有的状态(数据)抽离出来进行统一管理。
更多vuex用法参考:Vuex官网地址
provide/inject
provide/inject是Vue2.2.0后新增的API
只要一个组件使用了provide向下提供数据,那其下所有的子组件都可以通过 inject 来注入,不管中间隔了多少代,而且可以注入多个来自不同父级提供的数据。主要解决了子组件跨级获取上级组件的状态,建立主动提供与依赖注入的关系,但不是响应式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<!-- 父组件 -->
<template>
<div id="app">
<childCom />
</div>
</template>
<script>
import childCom from './childCom'
export default {
provide: { name: 'tew' }, // 将name值提供给子孙组件
components: { childCom }
}
</script>
<!-- 子组件 -->
<template>
<div id="app">
<div>子组件</div>
</div>
</template>
<script>
export default {
inject: ['name'], // 注入父组件或祖先组件的name
mounted () {
console.log(this.name); // tew
}
}
</script>
$parent/$children或#refs
通过$parent/$children或#refs获取到的是组件实例,使用组件实例可以直接调用组件的方法或数据
父组件访问子组件:使用$children或$refs
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
想明确获取其中一个特定的组件,可以使用$refs,通过ref给子组件绑定一个特定的ID,通过this.$refs.ID就可以访问到该组件了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31<template>
<div id="app">
<childCom ref="child" />
</div>
</template>
<script>
import Vue from 'vue'
export default {
data(){
return { msg: '父组件数据' }
},
components: {
"childCom": {
data () {
return { msg: '子组件数据' }
}
}
},
mounted (){
console.log(this.$refs.child.msg) // 子组件数据
console.log(this.$refs.child.$refs.grandson.msg) // 孙组件数据
console.log(this.$children[0].msg) // 子组件数据
console.log(this.$children[0].$children[0].msg) // 孙组件数据
}
}
Vue.component('grandsonCom', {
data(){ return { msg: '孙组件数据'} }
})
</script>子组件访问父组件:使用$parent
我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。避免直接访问父组件的数据,因为这样耦合度太高,父 组件中的状态将变得难以维护
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38<template>
<div id="app">
<parentCom />
</div>
</template>
<script>
import Vue from 'vue'
export default {
data() {
return { msg: 'grandpa组件数据' }
},
components: {
"parentCom": {
template: '<child-com />',
data () {
return { msg: 'parent组件数据' }
},
methods: {
handle(){ console.log('父组件方法') }
}
}
},
methods: {
handle(){ console.log('祖先组件方法') }
}
}
Vue.component('child-com', {
methods: {
showParent () {
console.log(this.$parent.msg) // parent组件数据
this.$parent.handle() // 父组件方法
console.log(this.$parent.$parent.msg) // grandpa组件数据
this.$parent.$parent.handle() // 祖先组件方法
}
}
})
</script>
dispatch/boardcast
vue1中有$dispatch和$boardcast两个方法,但vue2中被删除,可自己模拟,原理是通过$parent和$children来获取父组件和子组件,然后递归即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// dispatch 递归获取$parent
Vue.prototype.$dispatch = function (eventName, data) {
let parent = this.$parent // 查找父元素
while(parent){
if (parent) {
parent.$emit(eventName, data) // 父元素用$emit触发
parent = parent.$parent // 递归查找父元素
} else {
break
}
}
}
// boardcast
Vue.prototype.$boardcast = function(eventName, data){
boardcast.call(this, eventName, data)
}
function boardcast(eventName, data) {
this.$children.forEach(child => {
child.$emit(eventName, data)
if (child.$children.length) {
// 递归调用 通过call修改this指向 child
boardcast.call(child, eventName, data)
}
})
}
$attrs/$listeners
$attrs
:包含了父作用域中没有被props
接受的特性绑定(class和style除外)
。当一个组件没有声明任何prop
时,这里会包含所有父作用域的绑定(class和style除外)
,并且可以通过v-bind="$attrs"
传入内部组件。通常配合interitAttrs
选项一起使用。$listeners
:包含了父作用域中的(不含.native 修饰器的)v-on事件监听器
。它可以通过v-on="$listeners"
传入内部组件