Vue组件间通信的几种方式

news2024/11/19 22:28:16

Vue组件间通信的几种方式

  • 0、前言
  • 1、props/$emit(父子)
  • 2、ref / $refs(父子)
  • 3、provide / inject(深度父子)
  • 4、EventBus 事件总线 (任意两个组件通讯)
  • 5、$attrs / $listener(深度父子)
  • 6、$parent / $children
  • 7、vuex
    • 7.1 Vuex原理
    • 7.2 Vuex各模块功能
    • 7.3 Vuex与localStorage
  • 总结

0、前言

组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件可以有以下几种关系:

种类:

  1. 父子组件通信;
  2. 兄弟组件通信;
  3. 跨级组件通信

在这里插入图片描述

1、props/$emit(父子)

父组件通过 props 向子组件传递数据,在子组件中就可以用this.xxx方式使用,子组件通过 $emit 和父组件通信。

  1. props的特点:

● props只能是父组件向子组件进行传值,props使得父子组件之间形成一个单向的下行绑定。子组件的数据会随着父组件的更新而响应式更新。
● props可以显示定义一个或一个以上的数据,对于接收的数据,可以是各种数据类型,同样也可以是传递一个函数。
● props属性名规则:若在props中使用驼峰形式,模板中标签需要使用短横线的形式来书写。

  1. $emit的特点:

● $emit 绑定一个自定义事件,当这个事件被执行的时候就会将参数传递给父组件,而父组件通过v-on监听并接收参数。

// Parent.vue 组件
<template>
  <div>
    父组件
    <child :msg="msg" @changeTitle="updateTitle"></child>
    <h2>{{ parentTitle }}</h2>
  </div>
</template>

<script>

import Child from './Child.vue'
export default {
  name: 'Parent',
  components: { Child },
  data () {
    return {
      msg: 'hello',
      parentTitle: ''
    }
  },
  methods: {
    updateTitle (e) {
      this.parentTitle = e
    }
  },
};

</script>

<style scoped></style>
// Child.vue 组件
<template>
  <div>
    子组件
    {{ msg }}
    <button @click="handleChildClick">子组件按钮</button>
  </div>
</template>

<script>

export default {
  name: 'Child',
  props: {
    msg: {
      typeof: 'String',
      required: true
    }
  },
  data () {
    return {
      childTitle: '我是子组件传递过来的值'
    }
  },
  methods: {
    handleChildClick () {
      this.$emit('changeTitle', this.childTitle)
    }
  },
};

</script>

<style scoped></style>

2、ref / $refs(父子)

可实现父子组件之间的通信,常用于父组件中调用子组件的方法或者获取子组件的属性。

// Parent.vue 组件
<template>
  <div>
    父组件
    <child ref="child"></child>

  </div>
</template>

<script>

import Child from './Child.vue'
export default {
  name: 'Parent',
  components: { Child },
  data () {
    return {

    }
  },
  mounted () {
    const child = this.$refs.child
    console.log(child.name)
    child.changeName('调用子组件')
  },
};

</script>


<style scoped></style>
// Child.vue 组件
<template>
  <div>
    子组件
  </div>
</template>

<script>

export default {
  name: 'Child',
  data () {
    return {
      name: '我是子组件',
    }
  },
  methods: {
    changeName (msg) {
      console.log(msg)
    }
  },
};

</script>


<style scoped></style>

3、provide / inject(深度父子)

依赖注入,常见于插件或者组件库里。
多个组件嵌套时,顶层组件provide提供变量,后代组件都可以通过inject来注入变量。
缺点:传递的数据不是响应式的,inject接收到数据后,provide中的数据改变,但是后代组件中的数据不会改变。所以建议传一些常量或者方法。

// Parent.vue 组件
<template>
  <div>
    父组件
    <child ref="child"></child>

  </div>
</template>

<script>

import Child from './Child.vue'
export default {
  name: 'Parent',
  components: { Child },
  data () {
    return {

    }
  },
  // 方式1 不能获取methods中对的方法
  provide: {
    name: '我是父组件的值'
  }
  // 方式2 不能获取data中的属性
  provide () {
    return {
      name: '刘德华',
      childMethod: this.changeTitle
    }
  },
  methods: {
    changeTitle () {
      console.log('这是注册的方法')
    }
  },
};

</script>


<style scoped></style>
// Child.vue 组件
<template>
  <div>
    子组件
  </div>
</template>

<script>

export default {
  name: 'Child',
  data () {
    return {

    }
  },
  inject: ['name', 'childMethod'],
  mounted () {
    console.log(this.name)
    this.childMethod()
  }

};

</script>


<style scoped></style>

4、EventBus 事件总线 (任意两个组件通讯)

$emit 去监听,用$on去触发,注意需要$off来取消监听,否则可能会造成内存泄漏。
对于比较小型的项目,没有必要引入 vuex 的情况下,可以使用 eventBus。相比我们上面说的所有通信方式,eventBus 可以实现任意两个组件间的通信。

// 方法1: 抽离出一个单独的js文件 Bus.js,然后在需要的地方引入
import Vue from 'vue'
export default new Vue()

// 方法2:直接挂载到全局main.js中
import Vue from 'vue'
Vue.prototype.$bus = new Vue()

// 方法3:注入到Vue跟对象上 main.js
import Vue from 'vue'
new Vue({
	el: "#app",
	data: {
		Bus: new Vue()
	}
})

// Parent.vue 组件
<template>
  <div>
    <child></child>
    <button @click="handleClick">按钮</button>
  </div>
</template>

<script>
import Bus from './Bus.js'
import Child from './Child.vue'
export default {
  name: 'Parent',
  components: { Child },
  data () {
    return {

    }
  },
  methods: {
    handleClick () {
      // 自定义事件名
      Bus.$emit("sendMsg", "发送的内容")
    }
  },
};

</script>


<style scoped></style>

// Child.vue 组件
<template>
  <div>
  </div>
</template>

<script>
import Bus from './Bus.js'
export default {
  name: 'Child',
  data () {
    return {

    }
  },

  mounted () {
    // 监听事件的触发
    Bus.$on('sendMsg', data => {
      console.log('这是接受到的数据', data)
    })
  },
  beforeDestroy () {
    // 取消监听
    Bus.$off("sendMsg")
  }

};

</script>

<style scoped></style>

5、$attrs / $listener(深度父子)

多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。

// Parent.vue组件:
<template>
  <div>
    <child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="delInfo" />
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
  name: 'Parent',
  components: { Child },
  data () {
    return {
      name: '张三',
      age: 30,
      infoObj: {
        from: '浙江',
        job: 'policeman',
        hobby: ['reading', 'writing', 'skating']
      }
    }
  },
  methods: {
    updateInfo () {
      console.log('update info')
    },
    delInfo () {
      console.log('delete info')
    }
  }
};

</script>

<style scoped></style>
// Child.vue 组件
<template>
  <!-- 通过 $listeners 将父作用域中的事件,传入 grandSon 组件,使其可以获取到 father 中的事件 -->
  <grand-son :height="height" :weight="weight" @addInfo="addInfo" v-bind="$attrs" v-on="$listeners" />
</template>
<script>
import GrandSon from './GrandSon.vue'
export default {
  name: 'Child',
  components: { GrandSon },
  props: ['name'],
  data () {
    return {
      height: '180cm',
      weight: '72kg'
    }
  },
  created () {
    console.log(this.$attrs)
    // 结果:age, infoObj, 因为父组件共传来name, age, infoObj三个值,由于name被 props接收了,所以只有age, infoObj属性
    console.log(this.$listeners) // updateInfo: f, delInfo: f
  },
  methods: {
    addInfo () {
      console.log('add info')
    }
  }
}
</script>
// GrandSon.vue 组件
<template>
  <div>
    {{ $attrs }} --- {{ $listeners }}
  </div>
</template>
<script>
export default {
  props: ['weight'],
  created () {
    console.log(this.$attrs) // age, infoObj, height 
    console.log(this.$listeners) // updateInfo: f, delInfo: f, addInfo: f
    this.$emit('updateInfo') // 可以触发 father 组件中的updateInfo函数
  }
}
</script>

6、$parent / $children

// Parent.vue 组件
<template>
  <div class="hello_world">
    <div>{{ msg }}</div>
    <child></child>
    <button @click="changeA">点击改变子组件值</button>
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
  name: 'Parent',
  components: { Child },
  data () {
    return {
      msg: 'hello'
    }
  },
  methods: {
    changeA () {
      // 获取到子组件A
      console.log(this.$children)
      this.$children[0].messageA = 'this is new value'
    }
  }

};

</script>

<style scoped></style>
// Child.vue 组件
<template>
  <div class="com_a">
    <span>{{ messageA }}</span>
    <p>获取父组件的值为: {{ parentVal }}</p>
  </div>
</template>
<script>

export default {
  name: 'Child',
  data () {
    return {
      messageA: 'this is old'
    }
  },
  computed: {
    parentVal () {
      return this.$parent.msg
    }
  }

}
</script>

7、vuex

7.1 Vuex原理

Vuex是实现组件全局状态(数据)管理的一种机制,可以方便实现组件数据之间的共享;Vuex集中管理共享的数据,易于开发和后期维护;能够高效的实现组件之间的数据共享,提高开发效率;存储在Vuex的数据是响应式的,能够实时保持页面和数据的同步。
在这里插入图片描述

7.2 Vuex各模块功能

Vuex重要核心属性包括:state,mutations,action,getters,modules。
Vuex主要包括以下几个核心模块:

  1. State:用于数据的存储,是store中的唯一数据源;
  2. Getter:在 store 中定义“getter”(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
  3. Mutation:是唯一更改 store 中状态的方法,且必须是同步函数
  4. Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作
  5. Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中

7.3 Vuex与localStorage

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。

let defaultCity = "杭州"
try {   // 用户关闭了本地存储功能,此时在外层加个try...catch
  if (!defaultCity){
    defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))//在Vuex里保存的状态都是数组,而localStorage只支持字符串,所以需要用JSON转换
  }
}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 

总结

根据以上对这7种组件间的通信方法,可以将不同组件间的通信分为4种类型:父子组件间通信、跨代组件间通信、兄弟组件间通信、任意组件间通信

  1. 父子组件间通信
    子组件通过 props 属性来接受父组件的数据,然后父组件在子组件上注册监听事件,子组件通过 emit 触发事件来向父组件发送数据。
    通过 ref 属性给子组件设置一个名字。父组件通过 $refs 组件名来获得子组件,子组件通过 $parent 获得父组件,这样也可以实现通信。
    使用 provide/inject,在父组件中通过 provide提供变量,在子组件中通过 inject 来将变量注入到组件中。不论子组件有多深,只要调用了 inject 那么就可以注入 provide中的数据。
  2. 跨代组件间通信
    跨代组件间通信其实就是多层的父子组件通信,同样可以使用上述父子组件间通信的方法,只不过需要多层通信会比较麻烦。
    使用上述的7种方法的$attrs / $listeners方法。
  3. 兄弟组件间通信
    通过 $parent + $refs 以父组件为中间人来获取到兄弟组件,也可以进行通信。
  4. 任意组件间通信
    使用 eventBus ,其实就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通信的组件引入这个实例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递。
    如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候采用上面这一些方法可能不利于项目的维护。这个时候可以使用 vuex ,vuex 的思想就是将这一些公共的数据抽离出来,将它作为一个全局的变量来管理,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/691538.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

逻辑回归(Logistics Regression)的原理及实现

1.逻辑回归&#xff08;Logistics Regression&#xff09;的原理及实现 笔记来源于《白话机器学习的数学》 逻辑回归用于解决二分类问题 1.1 逻辑回归的原理 1.1.1 Sigmoid函数 sigmoid函数在神经网络中如何起作用&#xff1f;详见本人笔记&#xff1a;机器学习和AI底层逻辑 …

Spreadjs 16.1.3 + GcExcel 6.1.2 操作EXCEL好轻松-Crack

全球销量第一的 JavaScript 电子表格&#xff0c;包含 500 多个 Excel 函数 快速提供真正类似 Excel 的电子表格体验 - 对 Excel 零依赖。创建金融应用程序&#xff0c;仪表板,图表,数据透视表,性能基准,科学实验室笔记本&#xff0c;以及其他类似的 JavaScript 电子表格应用程…

Spring拦截器实现鉴权

什么是拦截器&#xff1f; 拦截器&#xff08;Interceptor&#xff09;类似于Servlet中的过滤器&#xff0c;主要用于拦截用户请求并做出相应的处理&#xff0c;例如拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。拦截器允许自定义预处理(Pre-Processing)…

Axure教程——滑屏效果

本文介绍用Axure来做一个移动端引导页的滑屏效果。 效果预览 预览地址&#xff1a;https://dsn3d3.axshare.com 制作元件 1、所需元件 矩形 动态面板 2、制作过程 拖入一个动态面板元件&#xff0c;命名为“”切换&#xff0c;大小设置为375667&#xff0c;如图&#xff1a; …

租房小程序源码推荐,让你的租房平台更有竞争力

为租房平台的从业者&#xff0c;你是否也曾为如何提高平台的竞争力而苦恼&#xff1f;租房小程序源码或许是一个不错的选择。 租房小程序源码是一种可以让你快速搭建一个专属的租房平台的工具&#xff0c;可以帮助你快速上线一个符合市场需求的租房平台。相较于从头开始开发一…

关于项目初期,数据量小的内容推荐的实现方法

前言 当下&#xff0c;只要是一个初具规模的内容应用都具备个性化推荐系统。比如购物类的会有推荐商品模块&#xff0c;搜索条下有个性化的搜索关键词或词条补全词&#xff0c;社交类的有博主推荐&#xff0c;视频或文章推荐等等。这些功能除了要有庞大的数据量&#xff0c;还要…

如何使用《水经注地图服务》快速发布TIF数据

《水经注地图服务》的快速发布功能是一个能够帮助用户实现快速发布地图服务的功能&#xff0c;并且提供常规情况下大多数用户所需的默认配置&#xff0c;让用户在发布地图时更加便捷。 今天为大家分享如何使用《水经注地图服务》快速发布TIF地图数据。 准备工作 离线示例数据…

navicat导入sql数据流程Database Navigator插件操作数据库

导入数据库流程&#xff1a; 选中localhost_3306&#xff0c;新建数据库 ->输入数据库名community ->选中community&#xff0c;新建查询 ->输入&#xff08;粘贴&#xff09;数据信息即可 Database Navigator插件操作数据库 IDEA插件系列&#xff08;6&#xff…

小研究 - Java 指针分析综述(一)

近年来静态程序分析已成为保障软件可靠性、安全性和高效性的关键技术之一. 指针分析作为基 础程序分析技术为静态程序分析提供关于程序的一系列基础信息&#xff0c;例如程序任意变量的指向关系、变量 间的别名关系、程序调用图、堆对象的可达性等. 介绍了 Java 指针分析的重要…

手撕code(3)

文章目录 迷宫最短路径和输出深度优先广度优先 48 旋转矩阵图像大数加减法146 LRU 缓存算法460 LFU 缓存算法 迷宫最短路径和输出 给定一个 n m 的二维整数数组&#xff0c;用来表示一个迷宫&#xff0c;数组中只包含 0 或 1 &#xff0c;其中 0 表示可以走的路&#xff0c;1…

MIT 6.S081 教材第六章内容 -- 锁 --上

MIT 6.S081 教材第六章内容 -- 锁 -- 上 引言锁竞态条件代码&#xff1a;使用锁死锁和锁排序锁和中断处理函数指令和内存访问排序睡眠锁真实世界思考 引言 MIT 6.S081 2020 操作系统 本文为MIT 6.S081课程第六章教材内容翻译加整理。 本课程前置知识主要涉及: C语言(建议阅读…

Java多线程——生命周期、并发、临界资源问题

目录 进程是什么&#xff1f; 线程是什么&#xff1f; 那这样做&#xff08;同时运行&#xff09;有什么优点呢&#xff1f;&#xff08;为什么要有多线程&#xff1f;&#xff09; 那什么时候需要使用多线程&#xff1f; 那线程和进程的关系是什么&#xff1f; 那线程和…

echarts的基础知识和配置项

异步数据加载和更新 ECharts 中在异步更新数据的时候需要通过series的name属性对应到相应的系列&#xff0c;如果没有name&#xff0c;series就会根据数组的顺序索引&#xff0c;把数据跟前面的配置对应上 loading动画 如果数据加载时间较长&#xff0c;一个空的坐标轴放在画…

无人值守远程访问软件

什么是无人值守远程访问 无人值守的远程访问使您能够控制无人监督且物理上远离的设备。它是企业管理员简化故障排除过程的基本解决方案。 无人值守的远程访问软件有什么作用 当最终用户可能无法在远程设备面前使用时&#xff0c;可能会有不同的情况。在这种情况下&#xff0…

CODESYS模拟量超限报警功能块

博途PLC模拟量超限报警功能块详细介绍请参看下面文章链接: PLC模拟量超限报警功能块_RXXW_Dor的博客-CSDN博客模拟量偏差报警功能块请参看下面文章:模拟量偏差报警功能块(SCL代码)_RXXW_Dor的博客-CSDN博客工业模拟量采集的相关基础知识,可以查看专栏的系列文章,这里不再赘…

CnOpenData·A股上市公司员工评价数据

一、数据简介 除了股东、债权人、政府等外部利益相关者外&#xff0c;员工的利益更应该得到公司的恰当保护&#xff0c;因为员工才是公司创造价值的真正主体。提高企业在产品市场的竞争力&#xff0c;首先就是要提高员工对企业的满意度&#xff0c;只有员工的满意度更高、幸福感…

Typora下载及激活及将图片上传到图床

Typora下载及激活及将图片上传到图床 为了解决在typora编辑图片后上传博客时&#xff0c;博客图片消失的问题&#xff0c;可以进行下面的操作 Typora下载及激活 typora下载及激活 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;znbw 解压缩后将winmm.dll文件放…

【零基础入门学习Python---Python中的文件操作教程】

&#x1f680; Python &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

王道考研数据结构代码总结(第六章)

目录 图基本定义拓扑排序 本文包含王道考研讲课中所涉及的数据结构中的所有代码&#xff0c;当PPT代码和书上代码有所区别时以咸鱼的PPT为主&#xff0c;个人认为PPT上的代码比王道书上的代码要便于理解&#xff0c;此外&#xff0c;本博客也许会补充一些额外的代码进来&#x…

【C++】STL关联式容器之map和set

【关联式容器】之map和set 容器类型树形结构的关联式容器mapset&#xff0c;multiset&#xff0c;multimap的区别与联系 容器类型 在STL中&#xff0c;我们接触过许多容器&#xff0c;例如&#xff1a;vector&#xff0c;list&#xff0c;stack&#xff0c;queue&#xff0c;m…