• vue组件间的通讯方式


    组件是Vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这意味着不同组件之间的数据无法相互进行直接的引用,所以组件间的相互通信是非常重要,面试题关于组件之间通信也挺多

    1.父子传参

    第一种:props、$emit

    // 父级给子级传
    <子级组件名 :value="value"></子级组件名>
    // 子级接收
    props: ['value'];  // 这里以字符串数组形式列出接收到的参数
    
    // 使用watch进行监听
    watch: {
        // value为基本数据类型
        value: {
            handler(newVal, oldVal) {
            	// 执行的操作
        	}
        }
        // value为数组时
        value: {
            immediate: true,
        	handler(newVal) {
            	// 执行的操作
        	}
        }
        // value为对象时
        value: {   // 如果监听value对象中某个属性的变化,需加上引号 'value.name'
            deep: true,
        	immediate: true,
        	handler(newVal) {
            	// 执行的操作
        	}
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    $emit:

    // 子传出
    this.$emit('submit(事件名)', value);
    // 父接收
    <之组件名 @submit></子组件名>
    methods: {
        submit(val) {
            console.log(val);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    第二种: ref/$refs

    ref:用在子组件上,通过实例来访问组建的数据和方法

    // 子组件中
    data() {
        return {
            name: "JS"
        }
    },
    methods: {
        sayHello() {
            console.log('Hello');
        }
    }
    
    // 父组件
    <template>
      <child ref="child"></child>    
    </template>
    <script>
      import child from '@/component/child.vue'      
      export default {
          components: { child },
          mounted() {
              console.log(this.$refs.child.name);  // JS
              this.$refs.child.sayHello();  // hello
          }
      }
    </script>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2.兄弟组件之间传值

    第一种:eventBus事件总线( e m i t / emit/ emit/on)

    适用于父子组件、非父子组件等之间的通信

    • 创建事件中心管理组件之间的通信
    import Vue from 'vue'
    export const EventBus = new Vue();
    
    • 1
    • 2
    • 发送事件,假设有两个兄弟组件mouYi和mouEr
    // 父组件
    import Vue from 'vue';
    export const EventBus = new Vue();
    
    • 1
    • 2
    • 3

    发送事件的两个组件:

    <template>
      <div>
        <mou-Yi></mou-Yi>
        <mou-Er></mou-Er>
      </div>
    </template>
    
    <script>
    import mouYi from './firstCom.vue'
    import mouEr from './secondCom.vue'
    export default {
      components: { mouYi, mouEr }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在mouYi发送事件:

    <template>
      <div>
        <button @click="add">加法</button>    
      </div>
    </template>
    
    <script>
    import {EventBus} from './event-bus.js' // 引入事件中心
    
    export default {
      data(){
        return{
          num:0
        }
      },
      methods:{
        add(){
          EventBus.$emit('addition', {
            num:this.num++
          })
        }
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在mouEr接收事件:

    <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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    利用eventBut作为一个桥梁,其他组价可以直接访问,如果需要后期维护,比较困难。

    props接收的方式和prop的验证

    推荐阅读 传送 了解prop的使用

    sync修饰符的使用

    具体使用 传送 sync修饰符的使用以及原理

    而使用.sync的区别在于

    // 第一:修改  “引入子组件"  的地方
    <child :isShow.sync="改变的值(要传递的值)"></child>
    // 其实是方法isShow的缩写
    @isShow ='(res) => isShow = res'
    
    // 第二:修改this.$emit
    props: ['isShow'];
    
    this.$emit('update: isShow', this.show)
    // update是固定的,isShow是我们要修改的状态值,与传入的状态名字相对应
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.eventBus事件总线 ($emit / $on)

    eventBus事件总线适用于父子组件、非父子组件等之间的通信,类似于Vuex

    • 创建事件中心管理组件之间的通信
    // eventBus.js
    
    import Vue from 'vue';
    export const EventBus = new Vue();
    
    • 1
    • 2
    • 3
    • 4
    • 发送/接收事件 假设有两个兄弟组件firstCom和secondCom

    他俩的父组件:

    <template>
        <div>
            <first-com>first-com>
            <second-com>second-com>
        div>
    template>
    
    <script>
    import firstCom from './相应路径'
    import secondCom from './相应路径'
    export default {
        components: { firstCom, secondCom }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在 firstCom 组件中发送事件:

    <template>
        <div>
            <button @click="add">点我相加button>
        div>
    template>
    
    <script>
    import { EventBus } from '../相应路径';  // 引入事件中心
    
    export default {
        data() {
            return {
                num: 0
            }
        },
        methods: {
            add() {
                EventBus.$emit('addFunc', {
                    num: this.num++
                })
            }
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在 secondCom 组件中接收事件:

    <template>
        <div>总数:{{ count }}div>
    template>
    
    <script>
    import { EventBus } from '../相应路径';
    
    export default {
        data() {
            return {
                count: 0
            }
        },
        methods: {
            EventBus.$on('addFunc', params => {
            	this.count += params.num;
        	})
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这么一看,只是将num存储于事件总线中,而事件总线扮演的是桥梁,只是建立沟通。但是如果项目过大,有多个传递,后期维护起来还是比较难受。

    4.依赖注入(provide / inject)

    用于父子、祖孙组件之间的通信,好处在于 不用一层一层的传递数据

    provideinject是Vue提供的两个钩子,和data、methods是同级(意思是data、methods在Vue里怎么写,这两个就怎么写)的,并且 provide 的书写形式和data一样

    • provide 钩子用来发送数据或方法
    • inject 钩子用来接收数据或方法

    用法:

    父组件中:

    provide() {
        return {
            num: this.num
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    子组件:

    inject: ['num'];
    
    // 还有另外一种写法,可以访问父组件中的所有属性
    provide() {
        return {
            app: this
        };
    }
    data() {
        return {
            num: 1
        };
    }
    
    inject: ['app'];
    console.log(this.app.num);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    **注意:**依赖注入 所提供的的属性是 非响应式的(非响应式属性的值发生改变不会触发视图更新);

    5.$parent / $children

    • 使用 $parent 可以让组件访问父组件的实例(访问的是上一级父组件的属性和方法)
    • 使用 c h i l d r e n 可以让组件访问子组件的实例,但是, children 可以让组件访问子组件的实例,但是, children可以让组件访问子组件的实例,但是,children并不能保证顺序,并且访问的数据也不是响应式的

    用法:

    子组件中:

    <template>
        <div>
            <span>{{ message }}span>
            <p>获取父组件的值为:{{ parentVal }}p>
        div>
    template>
    
    <script>
    export default {
        data() {
            return {
                message: 'Vue'
            }
        },
        computed: {
            parentVal() {
                return this.$parent.msg
            }
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    父组件中:

    <template>
        <div>
            <div>{{ msg }}div>
            <child>child>
            <button @click="change">点击改变子组件的值button>
        div>
    template>
    
    <script>
    import child from './相应路径';
    export default {
        components: { child },
        data() {
            return {
                msg: 'Welcome'
            }
        },
        methods: {
            // 获取子组件
            change() {
                this.$children[0].message = 'js'
            }
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    子组件获取父组件中的 parentVal 值,父组件改变了子组件中的 message 的值(上面代码中,其他都比较普通,只要是两个 this.$parentthis.$children

    注意:

    • 通过 $parent 访问到的是上一级父组件的实例,可以使用 $root 来访问根组件的实例
    • 在组件中使用 $children 得到的是所有的子组件的实例,它是一个数组并且是无序的
    • 在根组件 #app 上使用 $parent 得到的是 new Vue() 的实例,在这实例上再使用 $parent 得到的是 undefined ,而在最底层的子组件中使用 $children 是个空数组
    • $children 的值是数组,而 $parent 是个对象

    6.$attrs / $listeners

    场景:A是B组件的父组件,B是C的父组件;A(爷) > B(父) > C(孙),隔代传递数据用什么好

    方案:

    • 用props / $emit 来一级一级的传递,能够完成,但是比较复杂
    • 事件总线,但是在多人开发或者项目较大的时候,维护起来不是很方便
    • Vuex,如果仅仅是数据传递,浪费资源
    • 最好的办法: $attrs / $listeners 实现组件之间的跨代通信

    $attrs: 继承所有的父组件属性(除了props传递的属性、class和style),一般用在子组件的子元素上;

    l i s t e n e r s : 该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合 ‘ v − o n = " listeners: 该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合`v-on=" listeners:该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合von="listeners"` 将所有的事件监听器指向这个组件的某个特定的子元素(相当于子组件继承父组件的事件)。

    inheritAttrs 属性:

    • 默认值为true,继承所有的父组件属性除 props 之外的所有属性
    • 只继承class属性

    $attrs / $listeners 的用法

    A组件(APP.vue):

    <template>
        <div id="app">
        	// 这里监听两个事件,可以在B组件或者C组件中直接触发
            <child
            	:p-child1="child1"
                :p-child2="child2"
                @test1="onTest1"
                @test2="onTest2"
            >child>
        div>
    template>
    
    <script>
    import child1 from './相应路径';
    export default {
        components: { child1 },
        methods: {
            onTest1() {
                console.log('test1 running');
            },
            onTest2() {
                console.log('test2 running');
            }
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    B组件(即 child1.vue ):

    <template>
        <div class="child-1">
            <p>props: {{ pChild1 }}p>
            <p>$attrs: {{ $attrs }}p>
            <child2
            	v-bind="$attrs"        
                v-on="$listeners"
            >child2>
        div>
    template>
    <script>
    import Child2 from './相应路径';
    export default {
        props: ['pChild1'],
        components: { Child2 },
        inheritAttrs: false,  // 不继承class属性
        mounted() {
            this.$emit('test1');  // 触发APP.vue中的test1方法
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    C组件(即 child.vue ):

    <template>
        <div class="child2">
            <p>props: {{ pChild2 }}p>
            <p>$attrs: {{ $attrs }}p>
        div>
    template>
    
    <script>
    export default {
        props: ['pChild2'],
        inheritAttrs: false,
        mounted() {
            this.$emit('test2');  // 触发APP.vue中的test2方法
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. C组件中能直接触发 test 的原因在于B组件调用C组件时 使用 v-on 绑定了 $listence 属性;
    2. 在B组件中通过 v-bind 绑定 $attrs 属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的)

    小小总结

    其实不同组件之间的通信只有三种类型(也是面试中最常见的问题):父子组件间通信、兄弟组件间通信、非父子组件间通信

    1. 父子组件间通信(方法)
    • 子组件通过props属性来接收父组件的数据,然后父组件在子组件注册监听事件,子组件通过 emit 触发事件来向父组件发送数据;

    • 通过 ref 属性给予组件设置一个名字,父组件通过 $refs 组件名获取子组件,子组件通过 $parent 获得父组件;

    • 使用 provide/inject ,在父组件中通过provide 提供变量,在子组件中通过 inject 来将变量注入到组件中,这是一个不需要一层一层调用的方法;

    1. 兄弟组件间通信(方法)
    • 使用 eventBus 的方法,本质是通过创建一个空的 Vue实例来作为信息传递的桥梁,通信的组件引入这个实例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递
    • 通过 $parent / $refs 来获取兄弟组件,也可以通信
    1. 非父子组件间通信
    • 使用 eventBus ,事件总件
    • Vuex 全局的变量管理
  • 相关阅读:
    Unity 游戏开发、03 基础篇 | C#初级编程
    【分布式架构】单体优化遇瓶颈,负载均衡显神通
    c#实现单例模式的两种方法(饿汉式、懒汉式)
    【MySQL系列】 第四章 · 约束
    uniapp——实现电子签名功能——基础积累
    达梦数据库DM8的命令行工具DIsql介绍与使用
    Speedoffice(word)中如何添加批注内容
    【分享】Windows系统自动更新怎么永久停止?嘘!一般人我不告诉他,快进来看!
    C++算法前缀和的应用:得分最高的最小轮调的原理、源码及测试用例
    01背包面试题系列(一)
  • 原文地址:https://blog.csdn.net/weixin_45663702/article/details/126032778