vue组件通信的多种方式

组件是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": {
    template: '<grandsonCom ref="grandson" />',
    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', {
    template: '<p>{{msg}}</p>',
    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', {
    template: '<button @click="showParent">显示父元素信息</button>',
    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"传入内部组件
-------------本文结束感谢您的阅读-------------
0%