Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API 组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。其中将要实现的通信方式如下表所示

方式 Vue2 Vue3
父传子 props props
子传父 $emit emits
父传子 $attrs attrs
子传父 $listeners 无(合并到 attrs方式)
父传子 provide provide
子传父 inject inject
子组件访问父组件 $parent
父组件访问子组件 $children
父组件访问子组件 $ref expose&ref
兄弟传值 EventBus mitt

props

props是组件通信中最常用的通信方式之一。父组件通过v-bind传入,子组件通过props接收,下面是它的三种实现方式

  • 选项式API
  1. //父组件
  2. <template>
  3. <div>
  4. <Child :msg="parentMsg" />
  5. </div>
  6. </template>
  7. <script>
  8. import Child from './Child'
  9. export default {
  10. components:{
  11. Child
  12. },
  13. data() {
  14. return {
  15. parentMsg: '父组件信息'
  16. }
  17. }
  18. }
  19. </script>
  20. //子组件
  21. <template>
  22. <div>
  23. {{msg}}
  24. </div>
  25. </template>
  26. <script>
  27. export default {
  28. props:['msg']
  29. }
  30. </script>
  • 组合式Api
  1. //父组件
  2. <template>
  3. <div>
  4. <Child :msg="parentMsg" />
  5. </div>
  6. </template>
  7. <script>
  8. import { ref,defineComponent } from 'vue'
  9. import Child from './Child.vue'
  10. export default defineComponent({
  11. components:{
  12. Child
  13. },
  14. setup() {
  15. const parentMsg = ref('父组件信息')
  16. return {
  17. parentMsg
  18. };
  19. },
  20. });
  21. </script>
  22. //子组件
  23. <template>
  24. <div>
  25. {{ parentMsg }}
  26. </div>
  27. </template>
  28. <script>
  29. import { defineComponent,toRef } from "vue";
  30. export default defineComponent({
  31. props: ["msg"],// 如果这行不写,下面就接收不到
  32. setup(props) {
  33. console.log(props.msg) //父组件信息
  34. let parentMsg = toRef(props, 'msg')
  35. return {
  36. parentMsg
  37. };
  38. },
  39. });
  40. </script>
  • setup语法糖

  1. //父组件
  2. <template>
  3. <div>
  4. <Child :msg="parentMsg" />
  5. </div>
  6. </template>
  7. <script setup>
  8. import { ref } from 'vue'
  9. import Child from './Child.vue'
  10. const parentMsg = ref('父组件信息')
  11. </script>
  12. //子组件
  13. <template>
  14. <div>
  15. {{ parentMsg }}
  16. </div>
  17. </template>
  18. <script setup>
  19. import { toRef, defineProps } from "vue";
  20. const props = defineProps(["msg"]);
  21. console.log(props.msg) //父组件信息
  22. let parentMsg = toRef(props, 'msg')
  23. </script>

注意

props中数据流是单项的,即子组件不可改变父组件传来的值

在组合式API中,如果想在子组件中用其它变量接收props的值时需要使用toRef将props中的属性转为响应式。

emit

子组件可以通过emit发布一个事件并传递一些参数,父组件通过v-on进行这个事件的监听

  • 选项式API

  1. //父组件
  2. <template>
  3. <div>
  4. <Child @sendMsg="getFromChild" />
  5. </div>
  6. </template>
  7. <script>
  8. import Child from './Child'
  9. export default {
  10. components:{
  11. Child
  12. },
  13. methods: {
  14. getFromChild(val) {
  15. console.log(val) //我是子组件数据
  16. }
  17. }
  18. }
  19. </script>
  20. // 子组件
  21. <template>
  22. <div>
  23. <button @click="sendFun">send</button>
  24. </div>
  25. </template>
  26. <script>
  27. export default {
  28. methods:{
  29. sendFun(){
  30. this.$emit('sendMsg','我是子组件数据')
  31. }
  32. }
  33. }
  34. </script>
  • 组合式Api

  1. //父组件
  2. <template>
  3. <div>
  4. <Child @sendMsg="getFromChild" />
  5. </div>
  6. </template>
  7. <script>
  8. import Child from './Child'
  9. import { defineComponent } from "vue";
  10. export default defineComponent({
  11. components: {
  12. Child
  13. },
  14. setup() {
  15. const getFromChild = (val) => {
  16. console.log(val) //我是子组件数据
  17. }
  18. return {
  19. getFromChild
  20. };
  21. },
  22. });
  23. </script>
  24. //子组件
  25. <template>
  26. <div>
  27. <button @click="sendFun">send</button>
  28. </div>
  29. </template>
  30. <script>
  31. import { defineComponent } from "vue";
  32. export default defineComponent({
  33. emits: ['sendMsg'],
  34. setup(props, ctx) {
  35. const sendFun = () => {
  36. ctx.emit('sendMsg', '我是子组件数据')
  37. }
  38. return {
  39. sendFun
  40. };
  41. },
  42. });
  43. </script>
  • setup语法糖

  1. //父组件
  2. <template>
  3. <div>
  4. <Child @sendMsg="getFromChild" />
  5. </div>
  6. </template>
  7. <script setup>
  8. import Child from './Child'
  9. const getFromChild = (val) => {
  10. console.log(val) //我是子组件数据
  11. }
  12. </script>
  13. //子组件
  14. <template>
  15. <div>
  16. <button @click="sendFun">send</button>
  17. </div>
  18. </template>
  19. <script setup>
  20. import { defineEmits } from "vue";
  21. const emits = defineEmits(['sendMsg'])
  22. const sendFun = () => {
  23. emits('sendMsg', '我是子组件数据')
  24. }
  25. </script>

attrs和listeners

子组件使用$attrs可以获得父组件除了props传递的属性和特性绑定属性 (class和 style)之外的所有属性。

子组件使用$listeners可以获得父组件(不含.native修饰器的)所有v-on事件监听器,在Vue3中已经不再使用;但是Vue3中的attrs不仅可以获得父组件传来的属性也可以获得父组件v-on事件监听器

  • 选项式API

  1. //父组件
  2. <template>
  3. <div>
  4. <Child @parentFun="parentFun" :msg1="msg1" :msg2="msg2" />
  5. </div>
  6. </template>
  7. <script>
  8. import Child from './Child'
  9. export default {
  10. components:{
  11. Child
  12. },
  13. data(){
  14. return {
  15. msg1:'子组件msg1',
  16. msg2:'子组件msg2'
  17. }
  18. },
  19. methods: {
  20. parentFun(val) {
  21. console.log(`父组件方法被调用,获得子组件传值:${val}`)
  22. }
  23. }
  24. }
  25. </script>
  26. //子组件
  27. <template>
  28. <div>
  29. <button @click="getParentFun">调用父组件方法</button>
  30. </div>
  31. </template>
  32. <script>
  33. export default {
  34. methods:{
  35. getParentFun(){
  36. this.$listeners.parentFun('我是子组件数据')
  37. }
  38. },
  39. created(){
  40. //获取父组件中所有绑定属性
  41. console.log(this.$attrs) //{"msg1": "子组件msg1","msg2": "子组件msg2"}
  42. //获取父组件中所有绑定方法
  43. console.log(this.$listeners) //{parentFun:f}
  44. }
  45. }
  46. </script>
  • 组合式API

  1. //父组件
  2. <template>
  3. <div>
  4. <Child @parentFun="parentFun" :msg1="msg1" :msg2="msg2" />
  5. </div>
  6. </template>
  7. <script>
  8. import Child from './Child'
  9. import { defineComponent,ref } from "vue";
  10. export default defineComponent({
  11. components: {
  12. Child
  13. },
  14. setup() {
  15. const msg1 = ref('子组件msg1')
  16. const msg2 = ref('子组件msg2')
  17. const parentFun = (val) => {
  18. console.log(`父组件方法被调用,获得子组件传值:${val}`)
  19. }
  20. return {
  21. parentFun,
  22. msg1,
  23. msg2
  24. };
  25. },
  26. });
  27. </script>
  28. //子组件
  29. <template>
  30. <div>
  31. <button @click="getParentFun">调用父组件方法</button>
  32. </div>
  33. </template>
  34. <script>
  35. import { defineComponent } from "vue";
  36. export default defineComponent({
  37. emits: ['sendMsg'],
  38. setup(props, ctx) {
  39. //获取父组件方法和事件
  40. console.log(ctx.attrs) //Proxy {"msg1": "子组件msg1","msg2": "子组件msg2"}
  41. const getParentFun = () => {
  42. //调用父组件方法
  43. ctx.attrs.onParentFun('我是子组件数据')
  44. }
  45. return {
  46. getParentFun
  47. };
  48. },
  49. });
  50. </script>
  • setup语法糖

  1. //父组件
  2. <template>
  3. <div>
  4. <Child @parentFun="parentFun" :msg1="msg1" :msg2="msg2" />
  5. </div>
  6. </template>
  7. <script setup>
  8. import Child from './Child'
  9. import { ref } from "vue";
  10. const msg1 = ref('子组件msg1')
  11. const msg2 = ref('子组件msg2')
  12. const parentFun = (val) => {
  13. console.log(`父组件方法被调用,获得子组件传值:${val}`)
  14. }
  15. </script>
  16. //子组件
  17. <template>
  18. <div>
  19. <button @click="getParentFun">调用父组件方法</button>
  20. </div>
  21. </template>
  22. <script setup>
  23. import { useAttrs } from "vue";
  24. const attrs = useAttrs()
  25. //获取父组件方法和事件
  26. console.log(attrs) //Proxy {"msg1": "子组件msg1","msg2": "子组件msg2"}
  27. const getParentFun = () => {
  28. //调用父组件方法
  29. attrs.onParentFun('我是子组件数据')
  30. }
  31. </script>

注意

Vue3中使用attrs调用父组件方法时,方法前需要加上on;如parentFun->onParentFun

provide/inject

provide:是一个对象,或者是一个返回对象的函数。里面包含要给子孙后代属性

inject:一个字符串数组,或者是一个对象。获取父组件或更高层次的组件provide的值,既在任何后代组件都可以通过inject获得

  • 选项式API

  1. //父组件
  2. <script>
  3. import Child from './Child'
  4. export default {
  5. components: {
  6. Child
  7. },
  8. data() {
  9. return {
  10. msg1: '子组件msg1',
  11. msg2: '子组件msg2'
  12. }
  13. },
  14. provide() {
  15. return {
  16. msg1: this.msg1,
  17. msg2: this.msg2
  18. }
  19. }
  20. }
  21. </script>
  22. //子组件
  23. <script>
  24. export default {
  25. inject:['msg1','msg2'],
  26. created(){
  27. //获取高层级提供的属性
  28. console.log(this.msg1) //子组件msg1
  29. console.log(this.msg2) //子组件msg2
  30. }
  31. }
  32. </script>
  • 组合式API

  1. //父组件
  2. <script>
  3. import Child from './Child'
  4. import { ref, defineComponent,provide } from "vue";
  5. export default defineComponent({
  6. components:{
  7. Child
  8. },
  9. setup() {
  10. const msg1 = ref('子组件msg1')
  11. const msg2 = ref('子组件msg2')
  12. provide("msg1", msg1)
  13. provide("msg2", msg2)
  14. return {
  15. }
  16. },
  17. });
  18. </script>
  19. //子组件
  20. <template>
  21. <div>
  22. <button @click="getParentFun">调用父组件方法</button>
  23. </div>
  24. </template>
  25. <script>
  26. import { inject, defineComponent } from "vue";
  27. export default defineComponent({
  28. setup() {
  29. console.log(inject('msg1').value) //子组件msg1
  30. console.log(inject('msg2').value) //子组件msg2
  31. },
  32. });
  33. </script>
  • setup语法糖

  1. //父组件
  2. <script setup>
  3. import Child from './Child'
  4. import { ref,provide } from "vue";
  5. const msg1 = ref('子组件msg1')
  6. const msg2 = ref('子组件msg2')
  7. provide("msg1",msg1)
  8. provide("msg2",msg2)
  9. </script>
  10. //子组件
  11. <script setup>
  12. import { inject } from "vue";
  13. console.log(inject('msg1').value) //子组件msg1
  14. console.log(inject('msg2').value) //子组件msg2
  15. </script>

说明

provide/inject一般在深层组件嵌套中使用合适。一般在组件开发中用的居多。

parent/children

$parent: 子组件获取父组件Vue实例,可以获取父组件的属性方法等

$children: 父组件获取子组件Vue实例,是一个数组,是直接儿子的集合,但并不保证子组件的顺序

  • Vue2
  1. import Child from './Child'
  2. export default {
  3. components: {
  4. Child
  5. },
  6. created(){
  7. console.log(this.$children) //[Child实例]
  8. console.log(this.$parent)//父组件实例
  9. }
  10. }

注意

父组件获取到的$children并不是响应式的

expose&ref

$refs可以直接获取元素属性,同时也可以直接获取子组件实例

  • 选项式API

  1. //父组件
  2. <template>
  3. <div>
  4. <Child ref="child" />
  5. </div>
  6. </template>
  7. <script>
  8. import Child from './Child'
  9. export default {
  10. components: {
  11. Child
  12. },
  13. mounted(){
  14. //获取子组件属性
  15. console.log(this.$refs.child.msg) //子组件元素
  16. //调用子组件方法
  17. this.$refs.child.childFun('父组件信息')
  18. }
  19. }
  20. </script>
  21. //子组件
  22. <template>
  23. <div>
  24. <div></div>
  25. </div>
  26. </template>
  27. <script>
  28. export default {
  29. data(){
  30. return {
  31. msg:'子组件元素'
  32. }
  33. },
  34. methods:{
  35. childFun(val){
  36. console.log(`子组件方法被调用,值${val}`)
  37. }
  38. }
  39. }
  40. </script>
  • 组合式API

  1. //父组件
  2. <template>
  3. <div>
  4. <Child ref="child" />
  5. </div>
  6. </template>
  7. <script>
  8. import Child from './Child'
  9. import { ref, defineComponent, onMounted } from "vue";
  10. export default defineComponent({
  11. components: {
  12. Child
  13. },
  14. setup() {
  15. const child = ref() //注意命名需要和template中ref对应
  16. onMounted(() => {
  17. //获取子组件属性
  18. console.log(child.value.msg) //子组件元素
  19. //调用子组件方法
  20. child.value.childFun('父组件信息')
  21. })
  22. return {
  23. child //必须return出去 否则获取不到实例
  24. }
  25. },
  26. });
  27. </script>
  28. //子组件
  29. <template>
  30. <div>
  31. </div>
  32. </template>
  33. <script>
  34. import { defineComponent, ref } from "vue";
  35. export default defineComponent({
  36. setup() {
  37. const msg = ref('子组件元素')
  38. const childFun = (val) => {
  39. console.log(`子组件方法被调用,值${val}`)
  40. }
  41. return {
  42. msg,
  43. childFun
  44. }
  45. },
  46. });
  47. </script>
  • setup语法糖

  1. //父组件
  2. <template>
  3. <div>
  4. <Child ref="child" />
  5. </div>
  6. </template>
  7. <script setup>
  8. import Child from './Child'
  9. import { ref, onMounted } from "vue";
  10. const child = ref() //注意命名需要和template中ref对应
  11. onMounted(() => {
  12. //获取子组件属性
  13. console.log(child.value.msg) //子组件元素
  14. //调用子组件方法
  15. child.value.childFun('父组件信息')
  16. })
  17. </script>
  18. //子组件
  19. <template>
  20. <div>
  21. </div>
  22. </template>
  23. <script setup>
  24. import { ref,defineExpose } from "vue";
  25. const msg = ref('子组件元素')
  26. const childFun = (val) => {
  27. console.log(`子组件方法被调用,值${val}`)
  28. }
  29. //必须暴露出去父组件才会获取到
  30. defineExpose({
  31. childFun,
  32. msg
  33. })
  34. </script>

注意

通过ref获取子组件实例必须在页面挂载完成后才能获取。

在使用setup语法糖时候,子组件必须元素或方法暴露出去父组件才能获取到

EventBus/mitt

兄弟组件通信可以通过一个事件中心EventBus实现,既新建一个Vue实例来进行事件的监听,触发和销毁。

在Vue3中没有了EventBus兄弟组件通信,但是现在有了一个替代的方案mitt.js,原理还是 EventBus

  • 选项式API
  1. //组件1
  2. <template>
  3. <div>
  4. <button @click="sendMsg">传值</button>
  5. </div>
  6. </template>
  7. <script>
  8. import Bus from './bus.js'
  9. export default {
  10. data(){
  11. return {
  12. msg:'子组件元素'
  13. }
  14. },
  15. methods:{
  16. sendMsg(){
  17. Bus.$emit('sendMsg','兄弟的值')
  18. }
  19. }
  20. }
  21. </script>
  22. //组件2
  23. <template>
  24. <div>
  25. 组件2
  26. </div>
  27. </template>
  28. <script>
  29. import Bus from './bus.js'
  30. export default {
  31. created(){
  32. Bus.$on('sendMsg',(val)=>{
  33. console.log(val);//兄弟的值
  34. })
  35. }
  36. }
  37. </script>
  38. //bus.js
  39. import Vue from "vue"
  40. export default new Vue()
  • 组合式API

首先安装mitt

  1. npm i mitt -S

然后像Vue2中bus.js一样新建mitt.js文件

mitt.js

  1. import mitt from 'mitt'
  2. const Mitt = mitt()
  3. export default Mitt
  1. //组件1
  2. <template>
  3. <button @click="sendMsg">传值</button>
  4. </template>
  5. <script>
  6. import { defineComponent } from "vue";
  7. import Mitt from './mitt.js'
  8. export default defineComponent({
  9. setup() {
  10. const sendMsg = () => {
  11. Mitt.emit('sendMsg','兄弟的值')
  12. }
  13. return {
  14. sendMsg
  15. }
  16. },
  17. });
  18. </script>
  19. //组件2
  20. <template>
  21. <div>
  22. 组件2
  23. </div>
  24. </template>
  25. <script>
  26. import { defineComponent, onUnmounted } from "vue";
  27. import Mitt from './mitt.js'
  28. export default defineComponent({
  29. setup() {
  30. const getMsg = (val) => {
  31. console.log(val);//兄弟的值
  32. }
  33. Mitt.on('sendMsg', getMsg)
  34. onUnmounted(() => {
  35. //组件销毁 移除监听
  36. Mitt.off('sendMsg', getMsg)
  37. })
  38. },
  39. });
  40. </script>
  • setup语法糖

  1. //组件1
  2. <template>
  3. <button @click="sendMsg">传值</button>
  4. </template>
  5. <script setup>
  6. import Mitt from './mitt.js'
  7. const sendMsg = () => {
  8. Mitt.emit('sendMsg', '兄弟的值')
  9. }
  10. </script>
  11. //组件2
  12. <template>
  13. <div>
  14. 组件2
  15. </div>
  16. </template>
  17. <script setup>
  18. import { onUnmounted } from "vue";
  19. import Mitt from './mitt.js'
  20. const getMsg = (val) => {
  21. console.log(val);//兄弟的值
  22. }
  23. Mitt.on('sendMsg', getMsg)
  24. onUnmounted(() => {
  25. //组件销毁 移除监听
  26. Mitt.off('sendMsg', getMsg)
  27. })
  28. </script>

盘点Vue2和Vue3的10种组件通信方式(值得收藏)的更多相关文章

  1. vue中8种组件通信方式, 值得收藏!

    vue是数据驱动视图更新的框架, 所以对于vue来说组件间的数据通信非常重要,那么组件之间如何进行数据通信的呢? 首先我们需要知道在vue中组件之间存在什么样的关系, 才更容易理解他们的通信方式, 就 ...

  2. Vue中的8种组件通信方式

    Vue是数据驱动视图更新的框架,所以对于vue来说组件间的数据通信非常重要. 常见使用场景可以分为三类: 父子组件通信: props / $emit $parent / $children provi ...

  3. Vue中组件通信的几种方法(Vue3的7种和Vue2的12种组件通信)

    Vue3组件通信方式: props $emit expose / ref $attrs v-model provide / inject Vuex 使用方法: props 用 props 传数据给子组 ...

  4. vue2升级vue3:Vue Demij打通vue2与vue3壁垒,构建通用组件

    如果你的vue2代码之前是使用vue-class-component 类组件模式写的.选择可以使用 https://github.com/facing-dev/vue-facing-decorator ...

  5. Vue2和Vue3技术整理1 - 入门篇 - 更新完毕

    Vue2 0.前言 首先说明:要直接上手简单得很,看官网熟悉大概有哪些东西.怎么用的,然后简单练一下就可以做出程序来了,最多两天,无论Vue2还是Vue3,就都完全可以了,Vue3就是比Vue2多了一 ...

  6. Vue2和Vue3技术整理3 - 高级篇

    3.高级篇 前言 基础篇链接:https://www.cnblogs.com/xiegongzi/p/15782921.html 组件化开发篇链接:https://www.cnblogs.com/xi ...

  7. vue2升级vue3:vue2 vue-i18n 升级到vue3搭配VueI18n v9

    项目从vue2 升级vue3,VueI18n需要做适当的调整.主要是Vue I18n v8.x 到Vue I18n v9 or later 的变化,其中初始化: 具体可以参看:https://vue- ...

  8. vue2升级vue3指南(二)—— 语法warning&error篇

    本文总结了vue2升级vue3可能会遇到的语法警告和错误,如果想知道怎样升级,可以查看我的上一篇文章:vue2升级vue3指南(一)-- 环境准备和构建篇 Warning 1.deep /deep/和 ...

  9. 使用Enterprise Architecture绘制10种UML画画

    UML绘制10种课程要求UML画画,选Enterprise Architecture作为一个绘图工具,每一个草图必须是网上找教程,我觉得很麻烦,还有一些数字并没有找到详细的教程.在我自己找一个绘图方法 ...

随机推荐

  1. Java概论——JavaSE基础

    Java概论 Java特性和优势 简单性 面向对象 可移植性 高性能:即时编译 分布式:可处理TCP/IP协议的一些东西 动态性:通过反射机制使其具有动态性 多线程:良好的交互性和实时性 安全性:防病 ...

  2. c++ 快速乘

    First 在一些数学题中,两个数相乘运算很多,同时又很容易溢出,如两个 long long 相乘 今天本蒟蒻来总结一下快速乘的两种方法 1:二进制 和快速幂的原理一样,优化一个一个加的算法,复杂度\ ...

  3. NB-IoT无线通信模块与Lora无线通信协议技术分析与前景展望

    物联网的快速发展对无线通信技术提出了更高的要求,专为低带宽.低功耗.远距离.大量连接的物联网应用而设计的LPWAN(low-power Wide-Area Network,低功耗广域网)也快速兴起.物 ...

  4. 打字练习-编程语言关键字系列-java

    小编整理的java关键字,内容如下:abstract, assert, boolean, break, byte, case, catch, char, class, const, continue, ...

  5. 物联网?快来看 Arduino 上云啦

    作者:HelloGitHub-Anthony 这里是 HelloGitHub 推出的讲解开源硬件开发平台 Arduino 的系列教程. 第一篇:Arduino 介绍和开发环境搭建 第二篇:制作温湿度显 ...

  6. CentOS中实现基于Docker部署BI数据分析

    作为一个专业小白,咱啥都不懂. linux不懂,docker不懂. 但是我还想要完成领导下达的任务:在linux中安装docker后部署数据可视化工具.作为一名敬业 的打工人摆烂不可以,躺平不可以,弱 ...

  7. sap 获取设置的打印机参数

    *&---------------------------------------------------------------------* *& Form FRM_SET_PRI ...

  8. 深入理解 happens-before 原则

    在前面的文章中,我们深入了解了 Java 内存模型,知道了 Java 内存模型诞生的意义,以及其要解决的问题.最终我们知道:Java 内存模型就是定义了 8 个基本操作以及 8 个规则,只要遵守这些规 ...

  9. SQL Server数据库 备份A库,然后删除A库,再还原A库,此时数据库一直显示“正在还原”的解决方法

    SQL Server数据库 备份A库,然后删除A库,再还原A库,此时数据库一直显示"正在还原"的解决方法: A库一直显示"正在还原". 在这种状态下,由于未提交 ...

  10. NC14661 简单的数据结构

    NC14661 简单的数据结构 题目 题目描述 栗酱有一天在网上冲浪的时候发现了一道很有意思的数据结构题. 该数据结构形如长条形. 一开始该容器为空,有以下七种操作. 1 a从前面插入元素a 2 从前 ...