• 技术文章 >web前端 >Vue.js

    组件间怎么通信?盘点Vue组件通信方式(值得收藏)

    青灯夜游青灯夜游2022-08-19 20:04:02转载444
    Vue组件间怎么通信?本篇文章盘点Vue2和Vue3的10种组件通信方式,希望对大家有所帮助!

    大前端成长进阶课程:进入学习

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

    方式Vue2Vue3
    父传子propsprops
    子传父$emitemits
    父传子$attrsattrs
    子传父$listeners无(合并到 attrs方式)
    父传子provideprovide
    子传父injectinject
    子组件访问父组件$parent
    父组件访问子组件$children
    父组件访问子组件$refexpose&ref
    兄弟传值EventBusmitt

    props

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

    //父组件
    
    <template>
      <div>
        <Child :msg="parentMsg" />
      </div>
    </template>
    <script>
    import Child from './Child'
    export default {
      components:{
        Child
      },
      data() {
        return {
          parentMsg: '父组件信息'
        }
      }
    }
    </script>
    
    
    //子组件
    
    <template>
      <div>
        {{msg}}
      </div>
    </template>
    <script>
    export default {
      props:['msg']
    }
    </script>
    //父组件
    
    <template>
      <div>
        <Child :msg="parentMsg" />
      </div>
    </template>
    <script>
    import { ref,defineComponent } from 'vue'
    import Child from './Child.vue'
    export default defineComponent({
      components:{
        Child
      },
      setup() {
        const parentMsg = ref('父组件信息')
        return {
          parentMsg
        };
      },
    });
    </script>
    
    //子组件
    
    <template>
        <div>
            {{ parentMsg }}
        </div>
    </template>
    <script>
    import { defineComponent,toRef } from "vue";
    export default defineComponent({
        props: ["msg"],// 如果这行不写,下面就接收不到
        setup(props) {
            console.log(props.msg) //父组件信息
            let parentMsg = toRef(props, 'msg')
            return {
                parentMsg
            };
        },
    });
    </script>
    //父组件
    
    <template>
      <div>
        <Child :msg="parentMsg" />
      </div>
    </template>
    <script setup>
    import { ref } from 'vue'
    import Child from './Child.vue'
    const parentMsg = ref('父组件信息')
    </script>
    
    //子组件
    
    <template>
        <div>
            {{ parentMsg }}
        </div>
    </template>
    <script setup>
    import { toRef, defineProps } from "vue";
    const props = defineProps(["msg"]);
    console.log(props.msg) //父组件信息
    let parentMsg = toRef(props, 'msg')
    </script>

    注意

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

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

    emit

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

    //父组件
    
    <template>
      <div>
        <Child @sendMsg="getFromChild" />
      </div>
    </template>
    <script>
    import Child from './Child'
    export default {
      components:{
        Child
      },
      methods: {
        getFromChild(val) {
          console.log(val) //我是子组件数据
        }
      }
    }
    </script>
    
    // 子组件
    
    <template>
      <div>
        <button @click="sendFun">send</button>
      </div>
    </template>
    <script>
    export default {
      methods:{
        sendFun(){
          this.$emit('sendMsg','我是子组件数据')
        }
      }
    }
    </script>
    //父组件
    
    <template>
      <div>
        <Child @sendMsg="getFromChild" />
      </div>
    </template>
    <script>
    import Child from './Child'
    import { defineComponent } from "vue";
    export default defineComponent({
      components: {
        Child
      },
      setup() {
        const getFromChild = (val) => {
          console.log(val) //我是子组件数据
        }
        return {
          getFromChild
        };
      },
    });
    </script>
    
    //子组件
    
    <template>
        <div>
            <button @click="sendFun">send</button>
        </div>
    </template>
    
    <script>
    import { defineComponent } from "vue";
    export default defineComponent({
        emits: ['sendMsg'],
        setup(props, ctx) {
            const sendFun = () => {
                ctx.emit('sendMsg', '我是子组件数据')
            }
            return {
                sendFun
            };
        },
    });
    </script>
    //父组件
    
    <template>
      <div>
        <Child @sendMsg="getFromChild" />
      </div>
    </template>
    <script setup>
    import Child from './Child'
    const getFromChild = (val) => {
          console.log(val) //我是子组件数据
        }
    </script>
    
    //子组件
    
    <template>
        <div>
            <button @click="sendFun">send</button>
        </div>
    </template>
    <script setup>
    import { defineEmits } from "vue";
    const emits = defineEmits(['sendMsg'])
    const sendFun = () => {
        emits('sendMsg', '我是子组件数据')
    }
    </script>

    attrs和listeners

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

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

    //父组件
    
    <template>
      <div>
        <Child @parentFun="parentFun" :msg1="msg1" :msg2="msg2"  />
      </div>
    </template>
    <script>
    import Child from './Child'
    export default {
      components:{
        Child
      },
      data(){
        return {
          msg1:'子组件msg1',
          msg2:'子组件msg2'
        }
      },
      methods: {
        parentFun(val) {
          console.log(`父组件方法被调用,获得子组件传值:${val}`)
        }
      }
    }
    </script>
    
    //子组件
    
    <template>
      <div>
        <button @click="getParentFun">调用父组件方法</button>
      </div>
    </template>
    <script>
    export default {
      methods:{
        getParentFun(){
          this.$listeners.parentFun('我是子组件数据')
        }
      },
      created(){
        //获取父组件中所有绑定属性
        console.log(this.$attrs)  //{"msg1": "子组件msg1","msg2": "子组件msg2"}
        //获取父组件中所有绑定方法    
        console.log(this.$listeners) //{parentFun:f}
      }
    }
    </script>
    //父组件
    
    <template>
      <div>
        <Child @parentFun="parentFun" :msg1="msg1" :msg2="msg2" />
      </div>
    </template>
    <script>
    import Child from './Child'
    import { defineComponent,ref } from "vue";
    export default defineComponent({
      components: {
        Child
      },
      setup() {
        const msg1 = ref('子组件msg1')
        const msg2 = ref('子组件msg2')
        const parentFun = (val) => {
          console.log(`父组件方法被调用,获得子组件传值:${val}`)
        }
        return {
          parentFun,
          msg1,
          msg2
        };
      },
    });
    </script>
    
    //子组件
    
    <template>
        <div>
            <button @click="getParentFun">调用父组件方法</button>
        </div>
    </template>
    <script>
    import { defineComponent } from "vue";
    export default defineComponent({
        emits: ['sendMsg'],
        setup(props, ctx) {
            //获取父组件方法和事件
            console.log(ctx.attrs) //Proxy {"msg1": "子组件msg1","msg2": "子组件msg2"}
            const getParentFun = () => {
                //调用父组件方法
                ctx.attrs.onParentFun('我是子组件数据')
            }
            return {
                getParentFun
            };
        },
    });
    </script>
    //父组件
    
    <template>
      <div>
        <Child @parentFun="parentFun" :msg1="msg1" :msg2="msg2" />
      </div>
    </template>
    <script setup>
    import Child from './Child'
    import { ref } from "vue";
    const msg1 = ref('子组件msg1')
    const msg2 = ref('子组件msg2')
    const parentFun = (val) => {
      console.log(`父组件方法被调用,获得子组件传值:${val}`)
    }
    </script>
    
    //子组件
    
    <template>
        <div>
            <button @click="getParentFun">调用父组件方法</button>
        </div>
    </template>
    <script setup>
    import { useAttrs } from "vue";
    
    const attrs = useAttrs()
    //获取父组件方法和事件
    console.log(attrs) //Proxy {"msg1": "子组件msg1","msg2": "子组件msg2"}
    const getParentFun = () => {
        //调用父组件方法
        attrs.onParentFun('我是子组件数据')
    }
    </script>

    注意

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

    provide/inject

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

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

    //父组件
    <script>
    import Child from './Child'
    export default {
      components: {
        Child
      },
      data() {
        return {
          msg1: '子组件msg1',
          msg2: '子组件msg2'
        }
      },
      provide() {
        return {
          msg1: this.msg1,
          msg2: this.msg2
        }
      }
    }
    </script>
    
    //子组件
    
    <script>
    export default {
      inject:['msg1','msg2'],
      created(){
        //获取高层级提供的属性
        console.log(this.msg1) //子组件msg1
        console.log(this.msg2) //子组件msg2
      }
    }
    </script>
    //父组件
    
    <script>
    import Child from './Child'
    import { ref, defineComponent,provide } from "vue";
    export default defineComponent({
      components:{
        Child
      },
      setup() {
        const msg1 = ref('子组件msg1')
        const msg2 = ref('子组件msg2')
        provide("msg1", msg1)
        provide("msg2", msg2)
        return {
          
        }
      },
    });
    </script>
    
    //子组件
    
    <template>
        <div>
            <button @click="getParentFun">调用父组件方法</button>
        </div>
    </template>
    <script>
    import { inject, defineComponent } from "vue";
    export default defineComponent({
        setup() {
            console.log(inject('msg1').value) //子组件msg1
            console.log(inject('msg2').value) //子组件msg2
        },
    });
    </script>
    //父组件
    <script setup>
    import Child from './Child'
    import { ref,provide } from "vue";
    const msg1 = ref('子组件msg1')
    const msg2 = ref('子组件msg2')
    provide("msg1",msg1)
    provide("msg2",msg2)
    </script>
    
    //子组件
    
    <script setup>
    import { inject } from "vue";
    console.log(inject('msg1').value) //子组件msg1
    console.log(inject('msg2').value) //子组件msg2
    </script>

    说明

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

    parent/children

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

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

    import Child from './Child'
    export default {
      components: {
        Child
      },
      created(){
        console.log(this.$children) //[Child实例]
        console.log(this.$parent)//父组件实例
      }
    }

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

    expose&ref

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

    //父组件
    
    <template>
      <div>
        <Child ref="child" />
      </div>
    </template>
    <script>
    import Child from './Child'
    export default {
      components: {
        Child
      },
      mounted(){
        //获取子组件属性
        console.log(this.$refs.child.msg) //子组件元素
    
        //调用子组件方法
        this.$refs.child.childFun('父组件信息')
      }
    }
    </script>
    
    //子组件 
    
    <template>
      <div>
        <div></div>
      </div>
    </template>
    <script>
    export default {
      data(){
        return {
          msg:'子组件元素'
        }
      },
      methods:{
        childFun(val){
          console.log(`子组件方法被调用,值${val}`)
        }
      }
    }
    </script>
    //父组件
    
    <template>
      <div>
        <Child ref="child" />
      </div>
    </template>
    <script>
    import Child from './Child'
    import { ref, defineComponent, onMounted } from "vue";
    export default defineComponent({
      components: {
        Child
      },
    
      setup() {
        const child = ref() //注意命名需要和template中ref对应
        onMounted(() => {
          //获取子组件属性
          console.log(child.value.msg) //子组件元素
    
          //调用子组件方法
          child.value.childFun('父组件信息')
        })
        return {
          child //必须return出去 否则获取不到实例
        }
      },
    });
    </script>
    
    //子组件
    
    <template>
        <div>
        </div>
    </template>
    <script>
    import { defineComponent, ref } from "vue";
    export default defineComponent({
        setup() {
            const msg = ref('子组件元素')
            const childFun = (val) => {
                console.log(`子组件方法被调用,值${val}`)
            }
            return {
                msg,
                childFun
            }
        },
    });
    </script>
    //父组件
    
    <template>
      <div>
        <Child ref="child" />
      </div>
    </template>
    <script setup>
    import Child from './Child'
    import { ref, onMounted } from "vue";
    const child = ref() //注意命名需要和template中ref对应
    onMounted(() => {
      //获取子组件属性
      console.log(child.value.msg) //子组件元素
    
      //调用子组件方法
      child.value.childFun('父组件信息')
    })
    </script>
    
    //子组件
    
    <template>
        <div>
        </div>
    </template>
    <script setup>
    import { ref,defineExpose } from "vue";
    const msg = ref('子组件元素')
    const childFun = (val) => {
        console.log(`子组件方法被调用,值${val}`)
    }
    //必须暴露出去父组件才会获取到
    defineExpose({
        childFun,
        msg
    })
    </script>

    注意

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

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

    EventBus/mitt

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

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

    //组件1
    <template>
      <div>
        <button @click="sendMsg">传值</button>
      </div>
    </template>
    <script>
    import Bus from './bus.js'
    export default {
      data(){
        return {
          msg:'子组件元素'
        }
      },
      methods:{
        sendMsg(){
          Bus.$emit('sendMsg','兄弟的值')
        }
      }
    }
    </script>
    
    //组件2
    
    <template>
      <div>
        组件2
      </div>
    </template>
    <script>
    import Bus from './bus.js'
    export default {
      created(){
       Bus.$on('sendMsg',(val)=>{
        console.log(val);//兄弟的值
       })
      }
    }
    </script>
    
    //bus.js
    
    import Vue from "vue"
    export default new Vue()

    首先安装mitt

    npm i mitt -S

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

    mitt.js

    import mitt from 'mitt'
    const Mitt = mitt()
    export default Mitt
    //组件1
    <template>
         <button @click="sendMsg">传值</button>
    </template>
    <script>
    import { defineComponent } from "vue";
    import Mitt from './mitt.js'
    export default defineComponent({
        setup() {
            const sendMsg = () => {
                Mitt.emit('sendMsg','兄弟的值')
            }
            return {
               sendMsg
            }
        },
    });
    </script>
    
    //组件2
    <template>
      <div>
        组件2
      </div>
    </template>
    <script>
    import { defineComponent, onUnmounted } from "vue";
    import Mitt from './mitt.js'
    export default defineComponent({
      setup() {
        const getMsg = (val) => {
          console.log(val);//兄弟的值
        }
        Mitt.on('sendMsg', getMsg)
        onUnmounted(() => {
          //组件销毁 移除监听
          Mitt.off('sendMsg', getMsg)
        })
    
      },
    });
    </script>
    //组件1
    
    <template>
        <button @click="sendMsg">传值</button>
    </template>
    <script setup>
    import Mitt from './mitt.js'
    const sendMsg = () => {
        Mitt.emit('sendMsg', '兄弟的值')
    }
    </script>
    
    //组件2
    
    <template>
      <div>
        组件2
      </div>
    </template>
    <script setup>
    import { onUnmounted } from "vue";
    import Mitt from './mitt.js'
    const getMsg = (val) => {
      console.log(val);//兄弟的值
    }
    Mitt.on('sendMsg', getMsg)
    onUnmounted(() => {
      //组件销毁 移除监听
      Mitt.off('sendMsg', getMsg)
    })
    </script>

    写在最后

    其实组件还可以借助Vuex或者Pinia状态管理工具进行通信(但是组件之间的通信一般不建议这样做,因为这样就会出现组件不能复用的问题)。对于Vuex和Pinia的用法大家可以参考这篇文章一文解析Pinia和Vuex

    (学习视频分享:web前端开发编程基础视频

    以上就是组件间怎么通信?盘点Vue组件通信方式(值得收藏)的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    快捷开发Web应用及小程序:点击使用

    支持亿级表,高并发,自动生成可视化后台。

    专题推荐:组件通信 vue3 Vue
    上一篇:聊聊vite+vue3.0+ts中如何封装axios? 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• 聊聊vue3中setup函数的返回值• 详解vue3中setup函数的参数-props和context• 实例说明vue3中setup参数attrs,slots,emit是什么?• vue3 axios的使用介绍及数据渲染• 一文带你深入剖析vue3的响应式• 【整理分享】6 个 Vue3 开发必备的 VSCode 插件
    1/1

    PHP中文网