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) {
        	// 执行的操作
    	}
    }
}

$emit:

// 子传出
this.$emit('submit(事件名)', value);
// 父接收
<之组件名 @submit></子组件名>
methods: {
    submit(val) {
        console.log(val);
    }
}

第二种: 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>

2.兄弟组件之间传值

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

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

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

发送事件的两个组件:

<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>

在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>

在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>

利用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是我们要修改的状态值,与传入的状态名字相对应

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

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

  • 创建事件中心管理组件之间的通信
// eventBus.js

import Vue from 'vue';
export const EventBus = new Vue();
  • 发送/接收事件 假设有两个兄弟组件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>

在 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>

在 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>

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

4.依赖注入(provide / inject)

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

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

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

用法:

父组件中:

provide() {
    return {
        num: this.num
    }
}

子组件:

inject: ['num'];

// 还有另外一种写法,可以访问父组件中的所有属性
provide() {
    return {
        app: this
    };
}
data() {
    return {
        num: 1
    };
}

inject: ['app'];
console.log(this.app.num);

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

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>

父组件中:

<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>

子组件获取父组件中的 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>

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>

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. 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 全局的变量管理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@乐知者@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值