最近又读了一次Vue的官方文档,每次读感触都不一样,在看到禁用Attribute继承时有些不太理解,继而读了几篇帖子,突然意识到这不是平时面试时背的8种组件间的通信方式之一吗,但是项目中基本没用过;这里想归纳一下Vue组件间的几种通信方式,针对于一些难于理解的方式,之后会出具体的文章。

概述

Vue是数据驱动视图更新的框架, 所以对于Vue来说组件间的数据传递通信非常重要,那么组件之间如何进行数据通信的呢?在回答这问题之前我们要先了解组件之间都存在什么关系?

在组件树中可以看到:

  • AB、AC、AD、CE、DF、BG是父子组件
  • BCD 是兄弟组件
  • AE、AG、AF是祖孙组件

所以针对存在的3中组件关系我们也总结出了4类通信的方法:

  1. 父子组件通信: props / $emit$parent / $childrenprovide / injectref$attrs / $listeners;
  2. 兄弟组件通信: eventBusVuex
  3. 跨级通信: eventBusVuexprovide / inject$attrs / $listeners
  4. 访问根实例: $root

通信方式

props / emit

1. 父组件向子组件传值 prop

prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。

// section父组件
<template><div class="section"><com-article :articles="articleList"></com-article></div>
</template><script>
import comArticle from "./test/article.vue";
export default {name: "HelloWorld",components: { comArticle },data() {return {articleList: ["红楼梦", "西游记", "三国演义"]};}
};
</script>
// 子组件 article.vue
<template><div><span v-for="(item, index) in articles" :key="index">{{item}}</span></div>
</template><script>
export default {props: ["articles"]
};
</script>

总结:
prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单
向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。
注意
prop的类型校验
prop的命名

2. 子组件向父组件传值 emit

Vue 实例提供了一个自定义事件的系统。父级组件可以像处理 native DOM 事件一样通过 v-on 监听子组件实例的任意事件; 子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件,同时也可以传递第二个参数来给父组件传递数据。

// 父组件中
<template><div class="section"><com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article><p>{{currentIndex}}</p></div>
</template><script>
import comArticle from "./test/article.vue";
export default {name: "HelloWorld",components: { comArticle },data() {return {currentIndex: -1,articleList: ["红楼梦", "西游记", "三国演义"]};},methods: {onEmitIndex(idx) {this.currentIndex = idx;}}
};
</script>
// 在子组件中
<template><div><div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div></div>
</template><script>
export default {props: ["articles"],methods: {emitIndex(index) {this.$emit("onEmitIndex", index);   // 如果传递多个参数可以传递一个json}}
};
</script>

children/parent

$parent property 可以用来从一个子组件访问父组件的实例。它提供了一种机会,可以在后期随时触达父级组件,以替代将数据以 prop 的方式传入子组件的方式。

// 父组件中
<template><div class="hello_world"><div>{{msg}}</div><com-a></com-a><button @click="changeA">点击改变子组件值</button></div>
</template><script>
import ComA from "./test/comA.vue";
export default {name: "HelloWorld",components: { ComA },data() {return {msg: "Welcome"};},methods: {changeA() {// 获取到子组件Athis.$children[0].messageA = "this is new value";}}
};
</script>
// 子组件中
<template><div class="com_a"><span>{{messageA}}</span><p>获取父组件的值为: {{parentVal}}</p></div>
</template><script>
export default {data() {return {messageA: "this is old"};},computed: {parentVal() {return this.$parent.msg;}}
};
</script>

注意:

  1. $children 是一个数组,是直接儿子的集合,关于具体是第几个儿子,那么儿子里面有个 _uid 属性,可以知道他是第几个元素,是元素的唯一标识符,根据这个属性,我们可以进行其他的操作
  2. 当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。
  3. 当前实例的直接子组件。需要注意 children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。

provide/ inject 依赖注入

provide/ inject 是vue2.2.0新增的api, 简单来说就是父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。

由于$parent property 无法很好的扩展到更深层级的嵌套组件上。使得provideinject 得到了用武之地。

假设有三个组件: A.vue、B.vue、C.vue 其中 C是B的子组件,B是A的子组件

<template><div><comB></comB>{{ isShowRouter }}</div>
</template><script>
import comB from './b.vue'
export default {name: 'A',// provide 选项允许我们指定我们想要提供给后代组件的数据/方法。provide() {return {for: '这是组件',reload: this.reload}},components: {comB},data() {return{isShowRouter:true,}},methods: {reload(){this.isShowRouter = falsethis.$nextTick(()=>{this.isShowRouter = true})}}
}
</script>
// B.vue<template><div>{{ demo }}<comC></comC></div>
</template><script>
import comC from './c.vue'
export default {name: 'B',inject: ['for','reload'],components: {comC},data() {return {demo: this.for}}
}
</script>

// C.vue
<template><div>{{ demo }}</div>
</template><script>
export default {name: 'C',inject: ['for'],data() {return {demo: this.for};}
};
</script>

provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。有关provide详情可以看这里

ref / refs

访问子组件实例或子元素

// 子组件 A.vue
export default {data() {return {name: "Vue.js"};},methods: {sayHello() {console.log("hello");}}
};
// 父组件 app.vue
<template><component-a ref="comA"></component-a>
</template>
<script>
export default {mounted() {const comA = this.$refs.comA;console.log(comA.name); // Vue.jscomA.sayHello(); // hello}
};
</script>

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。

eventBus

eventBus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
缺点就是当项目较大,就容易造成难以维护的灾难。
使用方式:

  1. 初始化
    首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它。
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
  1. 发送事件
    假设你有两个组件: additionNum 和 showNum, 这两个组件可以是兄弟组件也可以是父子组件;这里我们以兄弟组件为例:
<template><div><show-num-com></show-num-com><addition-num-com></addition-num-com></div>
</template><script>
import showNumCom from "./showNum.vue";
import additionNumCom from "./additionNum.vue";
export default {components: { showNumCom, additionNumCom }
};
</script>
// addtionNum.vue 中发送事件<template><div><button @click="additionHandle">+加法器</button></div>
</template><script>
import { EventBus } from "./event-bus.js";
console.log(EventBus);
export default {data() {return {num: 1};},methods: {additionHandle() {EventBus.$emit("addition", {num: this.num++});}}
};
</script>
  1. 接收事件
// showNum.vue 中接收事件<template><div>计算和: {{count}}</div>
</template><script>
import { EventBus } from './event-bus.js'
export default {data() {return {count: 0}},mounted() {EventBus.$on('addition', param => {this.count = this.count + param.num;})}
}
</script>
  1. 移除事件监听者
import { eventBus } from'event-bus.js'
EventBus.$off('addition', {})

如果使用不善,EventBus会是一种灾难,到底是什么样的“灾难”了?大家都知道vue是单页应用,如果你在某一个页面刷新了之后,与之相关的EventBus会被移除,这样就导致业务走不下去。还要就是如果业务有反复操作的页面,EventBus在监听的时候就会触发很多次,也是一个非常大的隐患。这时候我们就需要好好处理EventBus在项目中的关系。通常会用到,在vue页面销毁时,同时移除EventBus事件监听。
有关Bus事件详细信息可以看这里

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.
Vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上。

  • state:用于数据的存储,是store中的唯一数据源
  • getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
  • mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
  • actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
  • modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护

localStorage / sessionStorage

这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。
通过window.localStorage.getItem(key)获取数据
通过window.localStorage.setItem(key,value)存储数据

注意用JSON.parse() / JSON.stringify() 做数据格式转换

localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题.

attrs与listeners

$listeners可以让你在孙子组件改变父组件的值:

<template><div><childcom :name="name" :age="age" :sex="sex" @testChangeName="changeName"></childcom></div>
</template>
<script>
export default {'name':'test',props:[],data(){return {'name':'张三','age':'30','sex':'男'}},components:{'childcom':{props:['name'],template:`<div><div>我是子组件   {{name}}</div><grandcom v-bind="$attrs" v-on="$listeners"></grandcom></div>`,components: {'grandcom':{template:`<div>我是孙子组件-------<button @click="grandChangeName">改变名字</button></div>`,methods:{grandChangeName(){this.$emit('testChangeName','kkkkkk')}}}}}},methods:{changeName(val){this.name = val}}
}
</script>

隔代传递数据的方法:

  • 使用props绑定来进行一级一级的信息传递, 如果D组件中状态改变需要传递数据给A, 使用事件系统一级级往上传递
  • 使用eventBus,这种情况下还是比较适合使用, 但是碰到多人合作开发时, 代码维护性较低, 可读性也低
  • 使用Vuex来进行数据管理, 但是如果仅仅是传递数据, 而不做中间处理,使用Vuex处理感觉有点大材小用了.

在vue2.4中,引入了attrs与listeners, 新增了inheritAttrs 选项。 在版本2.4以前,默认情况下,父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外),将会“回退”且作为普通的HTML特性应用在子组件的根元素上。接下来看一个跨级通信的例子:

// app.vue
// index.vue<template><div><child-com1 :name="name" :age="age" :gender="gender" :height="height" title="程序员成长指北"></child-com1></div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {components: { childCom1 },data() {return {name: "zhang",age: "18",gender: "女",height: "158"};}
};
</script>
// childCom1.vue
<template class="border"><div><p>name: {{ name}}</p><p>childCom1的$attrs: {{ $attrs }}</p><child-com2 v-bind="$attrs"></child-com2> </div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {components: {childCom2},inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性props: {name: String // name作为props属性绑定},created() {console.log(this.$attrs);// { "age": "18", "gender": "女", "height": "158", "title": "程序员成长指北" }}
};
</script>
// childCom2.vue
<template><div class="border"><p>age: {{ age}}</p><p>childCom2: {{ $attrs }}</p></div>
</template>
<script>
export default {inheritAttrs: false,props: {age: String},created() {console.log(this.$attrs);// { "gender": "女", "height": "158", "title": "程序员成长指北" }}
};
</script>

关于attrs和listeners 更多详细信息请参考这里

$root

在每个 new Vue 实例的子组件中,其根实例可以通过 $root property 进行访问。例如,在这个根实例中:

// Vue 根实例
new Vue({data: {foo: 1},computed: {bar: function () { /* ... */ }},methods: {baz: function () { /* ... */ }}
})

所有的子组件都可以将这个实例作为一个全局 store 来访问或使用。

// 获取根组件的数据
this.$root.foo// 写入根组件的数据
this.$root.foo = 2// 访问根组件的计算属性
this.$root.bar// 调用根组件的方法
this.$root.baz()

本文参考:vue的8种通信方式

Vue的8种通信方式相关推荐

  1. vue.js 三种方式安装--npm安装

    Vue.js是一个构建数据驱动的 web 界面的渐进式框架.     Vue.js 的目标是通过简单的 API 实现响应的数据绑定和组合的视图组件.它不仅易上手,便于与第三方库或既有项目整合.     ...

  2. java面试题9 牛客:不同的服务器之间,哪种通信方式是不可行的

    在一个基于分布式的游戏服务器系统中,不同的服务器之间,哪种通信方式是不可行的()? A管道 B消息队列 C高速缓存数据库 D套接字 首先看到这道题我是懵逼的,我们分别介绍一下各个的概念 管道为运行在同 ...

  3. python 进程间通信效率_(1)进程间几种通信方式

    管道.消息队列.共享内存.信号量.信号.socket 要知道管道.消息队列.共享内存的本质:内存本质.效率以及传输数据要求,各种使用方式 一.管道 管道通信效率低,不适合进程间频繁地交换数据.好处,简 ...

  4. 基于Linux的USB主/从设备之间的三种通信方式-转

    随着简单易用的接口日益流行,在中添加对USB接口的支持已成为大势所趋.本文通过介绍中支持USB的各种模块和库,分析了在Linux上利用USB实现高速串口和以太网连接等通信方式的具体方法. 通用串行总线 ...

  5. vue.js三种安装方式

    Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的渐进式框架.Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件.它不仅易于上手 ...

  6. webpack 安装vue(两种代码模式compiler 和runtime)

    使用webpack安装vue,import之后,运营项目报错,如下: [Vue warn]: You are using the runtime-only build of Vue where the ...

  7. 端系统及其两种通信方式

    端系统及其两种通信方式 端系统的概念 端系统通信方式--客户服务器方式 端系统通信方式--对等方式 端系统的概念 连接在互联网上的所有的主机称为 端系统. 端系统通信方式--客户服务器方式 客户服务器 ...

  8. 计算机两种通信方式-----串行通信和并行通信

    文章目录 计算机通信方式 一.串行总线 二.并行总线 两种通信方式的特点 并行通信: 计算机通信方式 计算机的两种基本通信方式是串行通信和并行通信. 串行通信是指在计算机总线或其他数据通道上,每次传输 ...

  9. linux配置usb主从_基于Linux的USB主/从设备之间的三种通信方式

    随着简单易用的USB接口日益流行,在嵌入式系统中添加对USB接口的支持已成为大势所趋.本文通过介绍Linux中支持USB的各种模块和库,分析了在Linux上利用USB实现高速串口和以太网连接等通信方式 ...

  10. 进程间的7种通信方式(含例程代码)

    下面的实验由我和我的同学完成的,这是操作系统课程的一个小实验,可以帮助理解进程间的通信. 进程间的通信 1.匿名管道 2.命名管道 3.消息队列 4.共享内存 5.信号 6.信号量 7.socket ...

最新文章

  1. qt发布后 mysql数据库_qt发布后 mysql数据库
  2. java 白皮书_java第三天学习内容回顾——java白皮书第四章
  3. 华为电脑c语言总是错误,关于华为机试题求代码!解决方法
  4. 手握价值70万录用书的程序员提离职,领导:你已升职成功,还走?
  5. C语言 -- 字符串中根据特定字符(串)分割
  6. 不用网关或代理的单点远程办公如何实现,Aruba推出EdgeConnect Microbranch
  7. python 保存图片代码_最简单的selenium+Python自动右键保存图片
  8. 查看Full GC方法:1,jconsel:2,linux 命令: jstat -gcutil id 4s 10003,linux 命令(前提有.gc日志):c
  9. 物联网哪个市场有可能诞生新一轮BAT?
  10. 学习笔记 9.22 - 9.29
  11. 信号与系统学习之第一章(系统的六大基本性质定义与判别:无记忆性、可逆性、因果性、稳定性、时不变性、线性)
  12. 用HTML简单制作一个网页
  13. Java方法变量中的final
  14. 将C盘里的Users文件夹移动到D盘
  15. handbrake中文版下载 | HandBrake(大菠萝视频格式转换器)官方中文版V1.3.3视频格式转换器哪个最好用
  16. LiveData 使用及原理解析
  17. 80 after generation to marry or not to marry is a question
  18. Deep Graph Kernels
  19. AndroidFragment仿写美团外卖界面
  20. NPOI导出Excel并下载到客户端

热门文章

  1. python 怎么爬桌软件数据_python爬虫怎么从软件界面抓取数据?
  2. 液晶面板的表面缺陷及其检测方法
  3. 第三次作业——K米评测
  4. python ccf题解 201903-2 二十四点
  5. M1芯片,PS2022版真的来了。Photoshop 2022 for Mac中文版介绍安装教程,支持Monterey系统
  6. 安卓:股票筛选及分析系统
  7. linux 内核配置ip地址,linux内核IP地址转换函数
  8. Java | PTA练习:Employee类的层级结构
  9. Win7 开启自带WIFI进行手机抓包
  10. 利用继电器实现防抖自锁功能