vue组件间通信方式: props
、$emit
、$refs
、EventBus
、$parent/$children
、$attrs/$listeners
、provide/inject
、vuex
。
组件之间的关系大致归类:
- 父子组件通信
- 非父子组件之间的通信(兄弟组件、隔代关系组件等)
1、props
/ $emit
1.1 父组件传子组件props
- 子组件设置
props
属性,定义接受父组件传递过来的参数。 - 父组件在使用子组件的标签中通过字面量来传递值。
// Father.vue
<Children name='jack' age=20 />
// Children.vue
// props:['name','age']
props:{
name: String,
age:{
type: Number, // 数据类型
require: true, // 是否必须传递
defaule: 18 // 可设默认值
}
}
:::details 查看示例
Father.vue
父组件
<template>
<div id="app">
<Children :users="users"></Children>
</div>
</template>
<script>
import Children from "./components/Children"
export default {
name: 'App',
data(){
return{
users:["三儿","四儿","五儿"]
}
},
components:{ Children }
}
</script>
Children.vue
子组件
<template>
<div>
<ul>
<li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面
</ul>
</div>
</template>
<script>
export default {
name: 'Children',
props:{
users:{ // 这个就是父组件中子标签自定义名字
type:Array, // 接受的数据类型
required:true // 是否必须传递
}
}
}
</script>
:::
1.2 子组件传父组件$emit
- 语法:
this.$emit(eventNamem [...age])
- 子组件通过
$emit
触发自定义事件(eventName),第二个参数为传递的数据。 - 父组件绑定监听器获取子组件传递过来的参数。
//
this.$emit('add', date)
// Father.vue
<Children @add='addDate($event)'>
::: details 查看示例
Children.vue
子组件
<template>
<div>
<h1 @click="changeTitle">{{title}}</h1>
</div>
</template>
<script>
export default {
name: 'Children',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子向父组件传值");
}
}
}
</script>
Father.vue
父组件
<template>
<div id="app">
<Children @titleChanged="updateTitle" ></Children>
//与子组件titleChanged自定义事件保持一致,updateTitle($event)接受传递过来的文字
<h2>{{title}}</h2>
</div>
</template>
<script>
import Children from "./components/Children"
export default {
name: 'App',
data(){
return{
title:"传递的是一个值"
}
},
methods:{
updateTitle(e){ //声明这个函数
this.title = e;
}
},
components:{ Children }
}
</script>
:::
2、$refs
/ $parent
::: tip
- 这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。
- 弊端是:无法在跨级或兄弟间通信。
:::
2.1 ref / $refs
- 父组件在子组件上设置
refName
:<Children ref='foo'>
- 父组件通过
$refs
获得子组件的数据:this.$refs.[refName]
::: details 查看示例
// 子组件
export default {
data(){
return{
title:"子组件 Data"
}
},
methods:{
getDate(){ //声明这个函数
console.log('子组件 getDate')
}
}
}
// 父组件
<template>
<children ref="son"></children>
</template>
<script>
export default {
...
...
mounted () {
// 获取子组件的属性
console.log(this.$refs.son.title);
// 获取子组件的方法
this.$refs.son.getDate()
}
}
</script>
:::
2.2 $parent / $children
::: tip
官方警示:节制使用,它们的主要目的是作为访问组件的应急方法。更推荐使用props
和events
实现父子组件通信
:::
this.$parent
可直接访问该组件的父实例或组件- 父组件也可以通过
this.$children
访问它所有的子组件。需注意它,返回的是一个子组件的集合(并不保证顺序,也不是响应式的)。想获得哪个组件的属性和方法可以通过:this.$children[index].子组件属性/方法
。
::: details 查看示例
parent.vue
<template>
<div>
<children-a></children-a>
<children-b></children-b>
</div>
</template>
<script>
import ChildrenA from "./components/ChildrenA"
import ChildrenB from "./components/ChildrenB"
export default {
name: 'App',
data(){
return{
title:"父组件title"
}
},
components:{ ChildrenA, ChildrenB },
mounted(){
// 通过 $children 来获取子组件的属性/方法
this.$children[0].getChildren(); // Children-A组件
console.log(this.$children[0].msg); // ChildrenA
console.log(this.$children[1].msg); // ChildrenB
},
methods:{
getQuery(e){ // 供子组件访问
console.log('parent', e)
}
}
}
</script>
Children-A/B.vue
// A.vue
<template>
<div>
<button @click='parentClik'>访问父组件</button>
</div>
</template>
<script>
export default{
data(){
return {
msg: 'ChildrenA'
}
},
methods:{
parentClick(){
this.$parent.getQuery('AAA') // 调用父组件内的方法
console.log(this.$parent.title) // 获取父组件的属性
},
getChildren(){
console.log('Chlidren-A组件')
}
}
}
</script>
// B.vue
<template>
<div>
ChildrenB
</div>
</template>
<script>
export default{
data(){
return {
msg: 'ChildrenB'
}
}
}
</script>
:::
$root
和$parent
都能实现访问父组件的属性和方法,二者区别在于:如果存在多级子组件,通过parent
访问得到的是它最近一级的父组件,通过root
访问得到的是根父组件
3、EventBus
eventBus
又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念。就像是所有组件公用相同的事件中心,可以向该中心注册发送事件或接收事件,所有组件都可以通知其他组件。参考地址
这种方法适用于小项目,项目复杂时会难以维护,这时推荐使用
Vuex
。
- 使用场景:兄弟组件传值
- 创建一个中央时间总线
EventBus
- 兄弟组件通过
$emit
触发自定义事件,$emit
第二个参数为传递的数值 - 另一个兄弟组件通过
$on
监听自定义事件
// main.js
Vue.prototype.$bus = new Vue()
// A.vue 发送消息
this.$bus.$emit('foo', 'A页面的信息')
// B.vue 监听接受消息
this.$bus.$on('foo', (msg) => { console.log(msg)})
可以使用 this.$bus.$off('sendMsg')
来移除应用内所有对此某个事件的监听(beforeDestroy()
)。
或者直接调用 this.$bus.$off()
来移除所有事件频道,不需要添加任何参数 。
4、$attrs/$listeners
- 使用场景:多级嵌套组件传递数据(祖先组件传递数据到子孙组件)
$attrs
:- 包含了父作用域中不被
prop
所识别 (且获取) 的特性绑定 (class
和style
除外)。 - 当一个组件没有声明任何
prop
时,这里会包含所有父作用域的绑定 (class
和style
除外),并且可以通过v-bind="$attrs"
传入内部组件。通常配合inheritAttrs
选项一起使用。
- 包含了父作用域中不被
$listeners
:- 包含了父作用域中的 (不含
.native
修饰符)v-on
事件监听器。它可以通过v-on=”$listeners”
传入内部组件。 - 它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件。
- 包含了父作用域中的 (不含
4.1 $attrs
// child:并未在props中声明foo
<p>{{$attrs.foo}}</p>
// parent
<HelloWorld foo="foo"/>
4.2 $listeners
// 给Grandson隔代传值,communication/index.vue
<Child2 msg="lalala" @some-event="onSomeEvent"></Child2>
// Child2做展开
<Grandson v-bind="$attrs" v-on="$listeners"></Grandson>
// Grandson使⽤
<div @click="$emit('some-event', 'msg from grandson')">
{{msg}}
</div>
::: details 查看示例
父组件
<template>
<div>
<Test :table="table" :sonVal="sonVal" :title="title" @getData="getData" @handleVal="handleVal" />
</div>
</template>
<script>
import Test from './test.vue';
export default {
data() {
return {
title: '组件props',
table: [
{ a1: '11', b1: '22' },
{ a2: '11', b2: '22' }
],
sonVal: 10
};
},
components: { Test },
methods: {
getData() {
console.log(this.title);
},
handleVal() {
// this.table.push({ a3: '11', b3: '22' });
this.sonVal = this.sonVal + 5;
}
}
};
</script>
子组件1
<template>
<div>
<h1>Test 组件: {{ title }}</h1>
<!-- 通过 $attrs(属性,除了【props中定义的属性】) -->
<!-- $listeners(方法)来给嵌套子组件传递父组件的属性和方法 -->
<son v-bind="$attrs" v-on="$listeners" />
</div>
</template>
<script>
// 引入子子组件
import son from './son';
export default {
props: ['title'],
components: {
son
},
created() {
console.log(this.$attrs); // {status: false}
console.log(this.$listeners); // {getData: ƒ}
}
};
</script>
嵌套子组件
<template>
<div>
<!-- 使用$attrs渲染数据 -->
<h1>Son 组件 {{ $attrs.table }}</h1>
<!-- $listeners分别调用绑定的方法 -->
<h1> {{ $attrs.sonVal }} <el-button @click="$listeners.handleVal">val+5</el-button>
</h1>
</div>
</template>
<script>
export default {
created() {
this.$emit('getData', 'Son组件的传值'); // 也可在此使用$emit调用方法
// console.log(this.$attrs) //{status: false}
// console.log(this.$listeners) // {getData: ƒ}
}
};
</script>
简单来说:$attrs
与 $listeners
是两个对象,$attrs
里存放的是父组件中绑定的非 Props
属性,$listeners
里存放的是父组件中绑定的非原生事件。
:::
5、provide/inject
可以很轻松的实现跨级访问组件数据,API详情见provide/inject
- 在祖先组件定义
provide
属性,返回传递的值;是一个对象或返回一个对象的函数 - 在后代组件通过
inject
接收组件传递过来的值;是一个字符串数组或一个对象
// 祖先组件
export default {
provide: {
name: '测试'
}
}
// 后代组件
export default {
inject: ['name'], // 获取到祖先组件传递过来的值
created () {
console.log(this.name); // 测试
}
}
:::tip
provide
和 inject
绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property
还是可响应的。
:::
6、Vuex
- 适用于业务逻辑复杂,很多组件之间需要同时处理一些公共的数据
- 作用相当于一个用来存储共享变量的容器 官链Vuex

state
用来存放共享变量的地方getter
可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值mutations
用来存放修改state
的方法。actions
也是用来存放修改state
的方法,不过action
是在mutations
的基础上进行。常用来做一些异步操作