最全的Vue组件通信方式总结
1、一图认清组件关系名词

- 父子关系:A与B、A与C、B与D、C与E
- 兄弟关系:B与C
- 隔代关系:A与D、A与E
- 非直系亲属:D与E
总结为三大类:
- 父子组件之间通信
- 兄弟组件之间通信
- 跨级通信
2、8种通信方式及使用总结
- props/- $emit
- $children/- $parent
- provide/- inject
- ref/- refs
- eventBus
- Vuex
- localStorage/- sessionStorage
- $attrs与- $listeners
常见使用场景可以分为三类:
- 父子组件通信:- props;- $parent/- $children;- provide/- inject;- ref;- $attrs/- $listeners
- 兄弟组件通信:- eventBus; vuex
- 跨级通信:- eventBus;Vuex;- provide/- inject、- $attrs/- $listeners
3、8种通信方式详解
- props/- $emit- 1. 父组件向子组件传值- 下面通过一个例子说明父组件如何向子组件传递数据:在子组件 - article.vue中如何获取父组件- section.vue中的数据- articles:['红楼梦', '西游记','三国演义']- // 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 只读,不可被修改,所有修改都会失效并警告。 
- 2. 子组件向父组件传值对于- $emit我自己的理解是这样的:- $emit绑定一个自定义事件, 当这个语句被执行时, 就会将参数arg传递给父组件,父组件通过v-on监听并接收参数。 通过一个例子,说明子组件如何向父组件传递数据。 在上个例子的基础上, 点击页面渲染出来的- ariticle的- item, 父组件中显示在数组中的下标- // 父组件中 
 <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)
 }
 }
 }
 </script>
 
- $children/- $parent- 通过$parent和$children就可以访问组件的实例,拿到实例代表什么?代表可以访问此组件的所有方法和data。接下来就是怎么实现拿到指定组件的实例。// 父组件中 
 <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() {
 // 获取到子组件A
 this.$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>要注意边界情况,如在#app上拿$parent得到的是new Vue()的实例,在这实例上再拿$parent得到的是undefined,而在最底层的子组件拿$children是个空数组。也要注意得到$parent和$children的值不一样,$children的值是数组,而$parent是个对象
- 总结:上面两种方式用于父子组件之间的通信, 而使用props进行父子组件通信更加普遍; 二者皆不能用于非父子组件之间的通信
 
- 通过
- provide/- inject- provide/- inject是- vue2.2.0新增的api, 简单来说就是父组件中通过- provide来提供变量, 然后再子组件中通过- inject来注入变量。
- 注意: 这里不论子组件嵌套有多深, 只要调用了inject那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中回去数据
- // A.vue <template> 
 <div>
 <comB></comB>
 </div>
 </template> <script>
 import comB from '../components/test/comB.vue'
 export default {
 name: "A",
 provide: {
 for: "demo"
 },
 components:{
 comB
 }
 }
 </script>- // B.vue <template> 
 <div>
 {{demo}}
 <comC></comC>
 </div>
 </template> <script>
 import comC from '../components/test/comC.vue'
 export default {
 name: "B",
 inject: ['for'],
 data() {
 return {
 demo: this.for
 }
 },
 components: {
 comC
 }
 }
 </script>- // C.vue 
 <template>
 <div>
 {{demo}}
 </div>
 </template> <script>
 export default {
 name: "C",
 inject: ['for'],
 data() {
 return {
 demo: this.for
 }
 }
 }
 </script>
 
- ref/- refs- ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据, 我们看一个- ref来访问组件的例子:- // 子组件 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.js
 comA.sayHello(); // hello
 }
 }
 </script>
 
- eventBus- eventBus又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
- eventBus也有不方便之处, 当项目较大,就容易造成难以维护的灾难
- 在Vue的项目中怎么使用eventBus来实现组件之间的数据通信呢?具体通过下面几个步骤
- 1. 初始化- // event-bus.js import Vue from 'vue' 
 export const EventBus = new Vue()
- 2. 发送事件- <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>
- 3. 接收事件- // 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>- 这样就实现了在组件 - addtionNum.vue中点击相加按钮, 在- showNum.vue中利用传递来的- num展示求和的结果.
- 4. 移除事件监听者
 如果想移除事件的监听, 可以像下面这样操作:- import { eventBus } from 'event-bus.js'
 EventBus.$off('addition', {})
 
- Vuex- 1. Vuex介绍- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. Vuex 解决了 - 多个视图依赖于同一状态和- 来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上- 2. Vuex各个模块- state:用于数据的存储,是store中的唯一数据源
- getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
- mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
- actions:类似于- mutation,用于提交- mutation来改变状态,而不直接变更状态,可以包含任意异步操作
- modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护
 - 3. Vuex实例应用- // 父组件 <template> 
 <div id="app">
 <ChildA/>
 <ChildB/>
 </div>
 </template> <script>
 import ChildA from './components/ChildA' // 导入A组件
 import ChildB from './components/ChildB' // 导入B组件 export default {
 name: 'App',
 components: {ChildA, ChildB} // 注册A、B组件
 }
 </script>- // 子组件childA <template> 
 <div id="childA">
 <h1>我是A组件</h1>
 <button @click="transform">点我让B组件接收到数据</button>
 <p>因为你点了B,所以我的信息发生了变化:{{BMessage}}</p>
 </div>
 </template> <script>
 export default {
 data() {
 return {
 AMessage: 'Hello,B组件,我是A组件'
 }
 },
 computed: {
 BMessage() {
 // 这里存储从store里获取的B组件的数据
 return this.$store.state.BMsg
 }
 },
 methods: {
 transform() {
 // 触发receiveAMsg,将A组件的数据存放到store里去
 this.$store.commit('receiveAMsg', {
 AMsg: this.AMessage
 })
 }
 }
 }
 </script>- // 子组件 childB <template> 
 <div id="childB">
 <h1>我是B组件</h1>
 <button @click="transform">点我让A组件接收到数据</button>
 <p>因为你点了A,所以我的信息发生了变化:{{AMessage}}</p>
 </div>
 </template> <script>
 export default {
 data() {
 return {
 BMessage: 'Hello,A组件,我是B组件'
 }
 },
 computed: {
 AMessage() {
 // 这里存储从store里获取的A组件的数据
 return this.$store.state.AMsg
 }
 },
 methods: {
 transform() {
 // 触发receiveBMsg,将B组件的数据存放到store里去
 this.$store.commit('receiveBMsg', {
 BMsg: this.BMessage
 })
 }
 }
 }
 </script>- vuex的store.js - import Vue from 'vue' 
 import Vuex from 'vuex'
 Vue.use(Vuex)
 const state = {
 // 初始化A和B组件的数据,等待获取
 AMsg: '',
 BMsg: ''
 } const mutations = {
 receiveAMsg(state, payload) {
 // 将A组件的数据存放于state
 state.AMsg = payload.AMsg
 },
 receiveBMsg(state, payload) {
 // 将B组件的数据存放于state
 state.BMsg = payload.BMsg
 }
 } export default new Vuex.Store({
 state,
 mutations
 })vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。- let defaultCity = "上海" 
 try { // 用户关闭了本地存储功能,此时在外层加个try...catch
 if (!defaultCity){
 defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
 }
 }catch(e){}
 export default new Vuex.Store({
 state: {
 city: defaultCity
 },
 mutations: {
 changeCity(state, city) {
 state.city = city
 try {
 window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
 // 数据改变的时候把数据拷贝一份保存到localStorage里面
 } catch (e) {}
 }
 }
 })- 这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换: - JSON.stringify(state.subscribeList); // array -> string 
 JSON.parse(window.localStorage.getItem("subscribeList")); // string -> array
 
- localStorage/- sessionStorage- 这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。 通过window.localStorage.getItem(key)获取数据 通过window.localStorage.setItem(key,value)存储数据
- 注意用JSON.parse()/JSON.stringify()做数据格式转换localStorage/sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题.
 
- 这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。 通过
- $attrs与- $listeners
- 现在我们来讨论一种情况, 我们一开始给出的组件关系图中A组件与D组件是隔代关系, 那它们之前进行通信有哪些方式呢? - 使用props绑定来进行一级一级的信息传递, 如果D组件中状态改变需要传递数据给A, 使用事件系统一级级往上传递
- 使用eventBus,这种情况下还是比较适合使用, 但是碰到多人合作开发时, 代码维护性较低, 可读性也低
- 使用Vuex来进行数据管理, 但是如果仅仅是传递数据, 而不做中间处理,使用Vuex处理感觉有点大材小用了.
 - 在 - vue2.4中,为了解决该需求,引入了- $attrs和- $listeners, 新增了- inheritAttrs选项。 在版本2.4以前,默认情况下,父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外),将会“回退”且作为普通的HTML特性应用在子组件的根元素上。
- 使用
- $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。- $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
- 接下来看一个跨级通信的例子: - // 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>
 
额外补充:
V-model
父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,
子组件中通过this.$emit(‘input',val)自动修改v-model绑定的值,下面看个例子。
父组件:
<template>
<div>
<child v-model="total"></child>
<button @click="increse">增加5</button>
</div>
</template> <script>
import Child from "./child.vue"
export default {
components: {
Child
},
data: function () {
return {
total: 0
};
},
methods: {
increse: function () {
this.total += 5;
}
}
}
</script>
子组件:
<template>
<div>
<span>{{value}}</span>
<button @click="reduce">减少5</button>
</div>
</template> <script>
export default {
props: {
value: Number // 注意这里是value
},
methods: {
reduce: function(){
this.$emit("input", this.value - 5) // 事件为input
}
}
}
</script>
最全的Vue组件通信方式总结的更多相关文章
- 整理4种Vue组件通信方式
		整理4种Vue组件通信方式 重点是梳理了前两个,父子组件通信和eventBus通信,我觉得Vue文档里的说明还是有一些简易,我自己第一遍是没看明白. 父子组件的通信 非父子组件的eventBus通信 ... 
- Vue组件通信方式全面详解
		vue组件通信方式全面详解 众所周知,Vue主要思想就是组件化开发.因为,在实际的项目开发中,肯定会以组件的开发模式进行.形如页面和页面之间需要通信一样,Vue 组件和组件之间肯定也需要互通有无.共享 ... 
- Vue组件通信方式(一)
		组件与组件的关系,通常有父子关系,兄弟关系以及隔代关系. 针对不同的场景,如何选用适合的通信方式呢? (一) props/$emit parentComponent ==> childCompo ... 
- Vue组件通信方式(8种)
		1.一图认清组件关系名词 父子关系:A与B.A与C.B与D.C与E 兄弟关系:B与C 隔代关系:A与D.A与E 非直系亲属:D与E 总结为三大类: 父子组件之间通信 兄弟组件之间通信 跨级通信 2.8 ... 
- vue组件通信方式总结
		对于vue来说,组件之间的消息传递是非常重要的,下面是我对组件之间消息传递的各种方式的总结,总共有8种方式. 1. props和$emit 父组件向子组件传递数据是通过prop传递的,子组件传递数据给 ... 
- vue组件通信方式(多种方案)
		一.Props传递数据 components |-Grandson1.vue //孙子1 |-Grandson2.vue //孙子2 |-Parent.vue //父亲 |-Grandson1.vue ... 
- Vue组件间通信6种方式
		摘要: 总有一款合适的通信方式. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的 ... 
- python 全栈开发,Day90(Vue组件,前端开发工具包)
		昨日内容回顾 1. Vue使用 1. 生成Vue实例和DOM中元素绑定 2. app.$el --> 取出该vue实例绑定的DOM标签 3. app.$data --> 取出该vue实例绑 ... 
- 如何写好一个vue组件,老夫的一年经验全在这了【转】 v-bind="$attrs" 和 v-on="$listeners"
		如何写好一个vue组件,老夫的一年经验全在这了 一个适用性良好的组件,一种是可配置项很多,另一种就是容易覆写,从而扩展功能 Vue 组件的 API 来自三部分——prop.事件和插槽: prop 允许 ... 
随机推荐
- 高性能高并发网站架构,教你搭建Redis5缓存集群
			一.Redis集群介绍 Redis真的是一个优秀的技术,它是一种key-value形式的NoSQL内存数据库,由ANSI C编写,遵守BSD协议.支持网络.可基于内存亦可持久化的日志型.Key-Val ... 
- 写在Logg SAP项目上线之际
			根据大环境大行业的惯用做法,公司建立Logg品牌是在意料之中.毫无意外的,Logg也要上到SAP系统中. 其实按它的业务模式来说上SAP系统并不困难,早在几年前就已经有做过了.无非就是接单不生产,外包 ... 
- 分析RESTful API安全性及如何采取保护措施
			本文中讨论了API安全性和采用安全措施的重要性,如身份验证,API密钥,访问控制和输入验证. API设计的第一步是撰写接口文档 根据TechTarget(海外IT专业媒体)的定义,RESTful AP ... 
- spring源码解析之IOC容器(四)——属性注入
			上一篇跟踪了bean的创建过程,接下来,我们继续跟踪bean的属性填充的过程.先回到doCreateBean方法,代码如下: protected Object doCreateBean(final S ... 
- Java项目接入sso单点登录
			最近在落地cat(java开发的一款开源监控系统)接入公司的内部项目,其中有项需求是接入公司的sso单点登录系统.研究了公司之前java项目接入sso系统,大部分是采用spring框架,然后依赖spr ... 
- 【设计模式】结构型05组合模式(Composite Pattern)
			组合模式(Composite Pattern) 意图:将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 主要解决:它在我们 ... 
- 【Idea】JUnit单元测试%MODULE_WORKING_DIR%' does not exist
			Idea执行单元测试时报错:上午9:35 Error running 'MobileMessageImplTest.java': Cannot start process, the working d ... 
- zabbix自定义监控项数据类型错误
			问题描述 监控cpu使用率,脚本获取的值是浮点型 zabbix创建监控项时没有选数据类型,导致监控数据有问题. 查看 zabbix-server 日志: ::203016.768 error rea ... 
- ACM之路(转载)
			转载自:https://www.cnblogs.com/tianjintou/p/4139416.html 要注意,ACM的竞赛性强,因此自己应该和自己的实际应用联系起来. 适合自己的才是好的,有的人 ... 
- 在Winform开发框架中使用DevExpress的TreeList和TreeListLookupEdit控件
			DevExpress提供的树形列表控件TreeList和树形下拉列表控件TreeListLookupEdit都是非常强大的一个控件,它和我们传统Winform的TreeView控件使用上有所不同,我一 ... 
