Vue组件间通信方式到底有几种
1. 前言
Vue的一个核心思想就是组件化。所谓组件化,就是把页面拆分成多个组件 (component),每个组件依赖的 CSS、JavaScript、模板、图片等资源放在一起开发和维护。组件是资源独立的,组件在系统内部可复用,组件和组件之间可以嵌套。
我们在用 Vue 开发实际项目的时候,就是像搭积木一样,编写一堆组件拼装生成页面。那么组件之间必然少不了相互通信,而Vue也提供了组件间通信的多种方式,本篇文章就来盘点一下在Vue中组件间通信方式到底有几种?
2. props / $emit(常用)
props和$emit适用于父子组件间通信,父组件通过props的方式向子组件传递数据,而子组件可以通过$emit向父组件通信。
2.1 父组件用props向子组件传递数据
下面通过一个例子说明父组件如何使用props向子组件传递数据:
//父组件
<template>
<div>
<h1>父组件</h1>
<Son :brandList="brandList"></Son>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
name: 'Parent',
components: { Son },
data() {
return {
brandList: ['BMW', 'Benz', 'Audi']
}
}
}
</script>
// 子组件
<template>
<div>
<h1>子组件</h1>
<span v-for="(item, index) in brandList" :key="index">{{item}}</span>
</div>
</template>
<script>
export default {
name: 'Son',
props: ['brandList']
}
</script>
示例说明:
父组件在调用子组件Son时,需要把brandList传递给子组件,那么就在调用子组件时把需要传递的数据添加到子组件的调用标签上,如<Son :brandList="brandList"></Son>。
而在子组件中,用props属性来接收父组件传来的数据brandList,接收之后,子组件就可以在自己的模板中使用传来的数据了。
这样,就完成了父组件向子组件的数据传递。
2.2 子组件用$emit向父组件传递数据
下面通过一个例子说明子组件如何使用$emit向父组件传递数据:
// 子组件
<template>
<div>
<h1>子组件</h1>
<span v-for="(item, index) in brandList" :key="index" @click="emitBrand(item)"> {{item}}
</span>
</div>
</template>
<script>
export default {
name: 'Son',
props: ['brandList'],
methods: {
emitBrand(item){
this.$emit('onEmitBrand',item)
}
}
}
</script>
//父组件
<template>
<div>
<h1>父组件</h1>
<Son :brandList="brandList" @onEmitBrand="onEmitBrand"></Son>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
name: 'Parent',
components: { Son },
data() {
return {
brandList: ['BMW', 'Benz', 'Audi']
}
},
methods: {
onEmitBrand(item) {
console.log(`您选择的品牌是${item}`)
}
}
}
</script>
示例说明:
在子组件中,绑定了点击事件emitBrand,在这个点击事件中使用$emit广播了一个名字为onEmitBrand的消息,同时为该消息传递item参数。
在父组件中,调用子组件的同时,也监听了onEmitBrand消息,一旦收到onEmitBrand消息,就会执行对应的回调onEmitBrand函数,在该回调函数中可以接收到子组件广播消息时传递的item参数。
捋一下流程就是:子组件触发点击事件emitBrand后,此时广播onEmitBrand消息,同时携带数据item,而此时监听onEmitBrand消息的父组件就会出发回调onEmitBrand函数,同时获得携带的数据item。
这样,就完成了子组件向父组件的数据传递。
3. $parent / children
在父子组件中,使用$parent可以在子组件中获取父组件的实例,而使用$children可以在父组件中获取所有子组件实例组成的数组。既然能够拿到组件实例,就表明可以访问到此组件的所有东西。
下面通过一个例子来说明:
//父组件
<template>
<div>
<h1>父组件</h1>
<div>父组件值:{{msg}}</div>
<p>获取子组件值:{{this.$children[0].message}}</p>
<button @click="changSon">点击改变子组件值</button>
<hr/>
<h1>子组件</h1>
<Son></Son>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
name: 'Parent',
components: { Son },
data() {
return {
msg: 'hello world'
}
},
methods: {
changSon() {
this.$children[0].message = '父组件改变了子组件的值'
}
}
}
</script>
// 子组件
<template>
<div>
<h1>子组件</h1>
<div>子组件值:{{message}}</div>
<p>获取父组件值:{{this.$parent.msg}}</p>
<button @click="changeParent">点击改变父组件中的值</button>
</div>
</template>
<script>
export default {
name: 'Son',
data () {
return {
message: '这是子组件'
};
},
methods: {
changeParent(){
this.$parent.msg = '子组件改变了父组件的值'
}
}
}
</script>
示例说明:
在父组件中通过this.$children可以获取所有子组件实例组成的数组,然后可以通过数组下标的形式取出所需要的子组件实例,然后就可以访问或修改子组件的内容了。
同样,在子组件中通过this.$parent可以获取父组件的实例,然后就可以访问或修改子组件的内容了。
值得注意的边界情况是:
当获取$parent得到的是new Vue()根实例时,如果在根实例上再获取$parent将得到的是undefined,同样,在最底层的子组件中获取$children将得到的是个空数组。
3.1 扩展内容
如果我们想在子组件中与祖父组件,甚至更上层的组件通信时,此时就会出现this.$parent.$parent.$parent....,同样,当在上层组件中想与孙子组件,甚至更下层组件通信时,也会出现this.$children.$children.$children....,那么为了避免出现这种不优雅的情况,我们为此封装两个方法:$dispatch和$broadcast。
$dispatch——向上派发
Vue.prototype.$dispatch = function $dispatch(eventName, data) {
let parent = this.$parent;
while (parent) {
parent.$emit(eventName, data);
parent = parent.$parent;
}
};
如果子组件想向它的上层父级组件传递数据,我们就通过递归获取
this.$parent的方式,一层一层向上广播消息以及携带需要传递的数据,然后在目的组件上监听这个消息即可。$broadcast——向下广播
Vue.prototype.$broadcast = function $broadcast(eventName, data) {
const broadcast = function () {
this.$children.forEach((child) => {
child.$emit(eventName, data);
if (child.$children) {
$broadcast.call(child, eventName, data);
}
});
};
broadcast.call(this, eventName, data);
};
同理,如果父组件想向它的下层子级组件传递数据,我们就通过获取
this.$children的方式,一层一层向下广播消息以及携带需要传递的数据,然后在目的组件上监听这个消息即可。
4. provide / inject
provide 和 inject 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深。简单来说就是父组件中通过provide来提供数据, 然后在子组件中通过inject来接收提供的数据,同时不论子组件嵌套的有多深,只要父级组件通过provide提供了数据,那么子组件就能够通过inject来接收到。
下面通过一个例子来说明:
//父组件
<template>
<div>
<h1>父组件</h1>
<Son></Son>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
name: 'Parent',
components: { Son },
provide: {
NLRX: "难凉热血"
}
}
</script>
// 子组件
<template>
<div>
<h1>子组件</h1>
{{NLRX}}
<Grandson></Grandson>
</div>
</template>
<script>
import Grandson from './Grandson.vue'
export default {
name: 'Son',
components: { Grandson },
inject: ['NLRX']
}
</script>
// 孙子组件
<template>
<div>
<h1>孙子组件</h1>
{{NLRX}}
</div>
</template>
<script>
export default {
name: 'Grandson',
inject: ['NLRX']
}
</script>
示例说明:
上面示例中展示了两级嵌套的三个组件:父组件——>子组件——>孙子组件。在父组件中使用provide提供了NLRX:'难凉热血'变量后,在子组件和孙子组件中均能够使用inject接收到。
5. $attrs / listeners
在vue2.4中引入了$attrs 和$listeners ,同时新增了inheritAttrs选项,默认为true。这两个api也可以用于父子组件间的通信,其中$attrs向下传递属性,$listeners向下传递方法。
5.1 $attrs向下传递属性
$attrs包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。
下面通过一个例子来说明:
// 父组件
<template>
<div>
<h1>父组件</h1>
<Son
name="难凉热血"
age="18"
gender="男"
height="175"
motto="叩首问路,码梦为生!"
></Son>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
name: 'Parent',
components: { Son },
}
</script>
// 子组件
<template>
<div>
<h1>子组件</h1>
<!-- 可以通过v-bind="$attrs"将属性继续向下传递 -->
<Grandson v-bind="$attrs"></Grandson>
</div>
</template>
<script>
import Grandson from './Grandson.vue'
export default {
name: 'Son',
components: { Grandson },
props: {
name: String // name作为props属性绑定
},
inheritAttrs: false, // 此选项为false,组件根元素上的没有在props声明的属性可以被$attrs获取到,
// 若为true,则没有在props声明的属性将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上
created() {
console.log(this.$attrs);// { "age": "18", "gender": "男", "height": "175", "motto": "叩首问路,码梦为生!" }
}
}
</script>
// 孙子组件
<template>
<div>
<h1>孙子组件</h1>
</div>
</template>
<script>
export default {
name: 'Grandson',
props: {
age: String
},
created() {
console.log(this.$attrs); // { "name": "难凉热血", "gender": "男", "height": "175", "motto":"叩首问路,码梦为生!" }
}
}
</script>
示例说明:
当父组件调用子组件时,为子组件传递了除props选项接收的name属性之外的其他属性,如age、height等,而这些多余的属性在子组件中可以通过this.$attrs获取到,同时在子组件调用孙子组件时,通过为孙子组件添加v-bind="$attrs",可以将多余的属性继续向下传递。以达到父组件向子组件通信的目的。
5.2 $listeners向下传递方法
$listeners包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。
下面通过一个例子来说明:
// 父组件
<template>
<div>
<h1>父组件</h1>
<Son @click="()=>{consoloe.log('难凉热血')}"></Son>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
name: 'Parent',
components: { Son },
}
</script>
// 子组件
<template>
<div>
<h1>子组件</h1>
<!-- 可以通过v-on="$listeners"将方法继续向下传递 -->
<Grandson v-on="$listeners"></Grandson>
</div>
</template>
<script>
import Grandson from './Grandson.vue'
export default {
name: 'Son',
components: { Grandson },
created() {
this.listeners.click() // 难凉热血
}
}
</script>
// 孙子组件
<template>
<div>
<h1>孙子组件</h1>
</div>
</template>
<script>
export default {
name: 'Grandson',
created() {
this.listeners.click() // 难凉热血
}
}
</script>
示例说明:
与$attrs不同的是,$listeners是传递方法,将父组件的方法向下传递。
6. ref / $refs
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
下面通过一个例子来说明:
// 父组件
<template>
<div>
<h1>父组件</h1>
<Son ref='son'></Son>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
name: 'Parent',
components: { Son },
methods: {
a () {
const son = this.$refs.son;
console.log(son.name); // Son
console.log(son.message); // 这是子组件
son.sayHello(); // hello
}
}
}
</script>
// 子组件
<template>
<div>
<h1>子组件</h1>
</div>
</template>
<script>
export default {
name: 'Son',
data () {
return {
message: '这是子组件'
}
},
methods: {
sayHello () {
console.log('hello')
}
}
}
</script>
示例说明:
父组件在调用子组件时,为子组件添加了ref属性,那么在父组件中就可以通过this.$refs.son来获取到子组件的实例,这样就可以访问子组件上的东西了。
7. eventBus(常用)
eventBus 又称为事件总线,在vue中可以用它来作为组件之间通信的桥梁, 所有组件共用相同的事件中心,所有组件都用它来注册发送事件和接收事件,其实这就是一个典型的发布订阅模式,由各个组件向eventBus订阅事件,并由eventBus发布事件,对于小型不复杂的项目可以使用这种方式。
它的使用方式如下:
创建一个事件总线并将其导出
// event-bus.js import Vue from 'vue'
export const EventBus = new Vue()
发布事件
// A组件
<template>
<div>
<h1>A组件</h1>
<button @click="emitEvent">发布事件</button>
</div>
</template> <script>
import {EventBus} from './event-bus.js'
export default {
name: 'ComA',
methods: {
emitEvent () {
EventBus.$emit('nlrx', {
nlrx:'难凉热血'
})
}
}
}
</script>
创建一个A组件,并且在A组件中发布一个名为
nlrx的消息,消息内容是{nlrx:'难凉热血'}。订阅事件
// B组件
<template>
<div>
<h1>B组件</h1>
<button @click="receiveEvent">接收事件</button>
</div>
</template> <script>
import {EventBus} from './event-bus.js'
export default {
name: 'ComB',
mounted() {
EventBus.$on('nlrx', data => {
console.log(data.nlrx) // 难凉热血
})
}
}
</script>
创建一个B组件,并且在B组件中订阅监听
nlrx消息,同时回调函数中的data参数即是消息内容{nlrx:'难凉热血'}。
8. vuex(常用)
Vuex 是一个专为 Vue 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上。
关于Vuex的具体介绍以及使用方式可参见之前的一篇博文:通俗易懂了解Vuex!
9. 总结
以上就是盘点了Vue中组件间通信的七种方式,这些方式根据使用场景大致可分为:
- 父子组件通信:
props / $emit;$children / $parent;provide / inject;ref / refs;$attrs / $listeners - 兄弟组件通信:
eventBus;vuex - 跨级组件通信:
eventBus;Vuex;provide / inject
(完)
Vue组件间通信方式到底有几种的更多相关文章
- Vue组件间通信方式
一.Props传递数据 在父组件中使用子组件,本质通过v-bind绑定属性传入子组件,子组件通过props接收父组件传入的属性 <template> <div> 父组件:{{m ...
- Vue组件间通信6种方式
摘要: 总有一款合适的通信方式. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的 ...
- Vue.js组件间通信方式总结
平时在使用Vue框架的业务开发中,组件不仅仅要把模板的内容进行复用,更重要的是组件之间要进行通信.组件之间通信分为三种:父-子:子-父:跨级组件通信.下面,就组件间如何通信做一些总结. 1.父组件到子 ...
- vue 组件的通信方式(完整版)
几种通信方式无外乎以下几种: Prop(常用) $emit (组件封装用的较多) .sync语法糖 (较少) $attrs & $listeners (组件封装用的较多) provide &a ...
- Vue 组件间传值
前言 Vue 作为现在比较火的框架之一,相信您在使用的过程中,也会遇到组件间传值的情况,本文将讲解几种 Vue 组件间传值的几种方法,跟着小编一起来学习一下吧! 实现 注意: 学习本文,需要您对 Vu ...
- vue组件间通信六种方式(完整版)
本文总结了vue组件间通信的几种方式,如props. $emit/ $on.vuex. $parent / $children. $attrs/ $listeners和provide/inject,以 ...
- Vue组件间通信-Vuex
上回说到Vue组件间通讯,最后留了一个彩蛋~~~Vuex.Vuex是另一种组件通讯的方法,这节来说说Vuex(store仓库). 首先Vuex需要安装,安装的方式有很多,在这里就不一一细说了.我是通过 ...
- Vue 组件的通信方式都有哪些?
说一下 Vue 组件的通信方式都有哪些?(父子组件,兄弟组件,多级嵌套组件等等) 一.父组件向子组件传值 二.子组件向父组件传值 三.兄弟组件传值 四.跨组件 一.父组件向子组件传值 1.1props ...
- vue 组件间的几种通信方式
Props配置 原理:通过props配置,进行父子组件间的通信,跨父子通信需要其他组件进行过渡. 使用: 传递方在标签中添加传递内容 <Son :newName="name" ...
随机推荐
- Flutter中TabBarView切换状态保存
TabBarView 类似于Android中的viewPager,但是默认是没有实现切换分页状态保存的.估计是出于节约内存的原因吧. 发现这个问题的时候,搜索了一下全网.大致就两种解决方案,1是修改源 ...
- RMAN详细教程(二):备份、检查、维护、恢复
RMAN详细教程(一):基本命令代码 一.创建增量备份 增量备份级别为0-4,但为方便备份管理,oracle建议只限于0级和1级. 1.差异增量备份(differential incremental ...
- Spring Boot2 系列教程(十二)@ControllerAdvice 的三种使用场景
严格来说,本文并不算是 Spring Boot 中的知识点,但是很多学过 SpringMVC 的小伙伴,对于 @ControllerAdvice 却并不熟悉,Spring Boot 和 SpringM ...
- 树莓派(4B)Linux + .Net Core嵌入式-HelloWorld(二)
一.新建.Net Core项目 新建.Net Core3.0的控制台应用,代码如下 namespace Demo { class Program { static void Main(string[] ...
- php 学习编译扩展
原文 : http://kimi.it/496.html 系统环境 : Ubuntu 目标 : 可以像 php 提供的内部函数一样,使用 myecho 函数 : 输出如下 : 1. 获取 php 的源 ...
- DNS原理及解析过程详解
相信大家在平时工作中都离不开DNS解析,DNS解析是互联网访问的第一步,无论是使用笔记本浏览器访问网络还是打开手机APP的时候,访问网络资源的第一步必然要经过DNS解析流程.下面我们将详细的给大家讲解 ...
- 【Python秘籍】numpy到tensor的转换
在用pytorch训练神经网络时,我们常常需要在numpy的数组变量类型与pytorch中的tensor类型进行转换,今天给大家介绍一种它们之间互相转换的方法. 一.numpy到tensor 首先我们 ...
- Java8系列 (一) Lambda表达式
函数式编程 在介绍Lambda表达式之前, 首先需要引入另一个概念, 函数式编程. 函数式编程是一种编程范式, 也就是如何编写程序的方法论.它的核心思想是将运算过程尽量写成一系列嵌套的函数调用,关注的 ...
- vue系列---响应式原理实现及Observer源码解析(一)
_ 阅读目录 一. 什么是响应式? 二:如何侦测数据的变化? 2.1 Object.defineProperty() 侦测对象属性值变化 2.2 如何侦测数组的索引值的变化 2.3 如何监听数组内容的 ...
- 破解Android设备无法联调的谜题
这篇文章要感谢来自知乎的小伙伴:子非鱼,他最近被一件事情困惑,那就是:Android手机无法联调了.在解决完他的疑问后,突然意识到,其实自己在前一段时间也曾遇到同样的问题,最后居然还怀疑是电脑和手机不 ...