组件是Vue.js最强大的功能之一,而组件实例的作用域
是相互独立的,这意味着不同组件之间的数据无法相互进行直接的引用,所以组件间的相互通信是非常重要,面试题关于组件之间通信也挺多
第一种: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>
第一种:eventBus事件总线( e m i t / emit/ emit/on)
适用于父子组件、非父子组件等之间的通信
import Vue from 'vue'
export const EventBus = new Vue();
// 父组件
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作为一个桥梁,其他组价可以直接访问,如果需要后期维护,比较困难。
推荐阅读 传送 了解prop的使用
具体使用 传送 sync修饰符的使用以及原理
而使用.sync的区别在于
// 第一:修改 “引入子组件" 的地方
<child :isShow.sync="改变的值(要传递的值)"></child>
// 其实是方法isShow的缩写
@isShow ='(res) => isShow = res'
// 第二:修改this.$emit
props: ['isShow'];
this.$emit('update: isShow', this.show)
// update是固定的,isShow是我们要修改的状态值,与传入的状态名字相对应
eventBus事件总线适用于父子组件、非父子组件等之间的通信,类似于Vuex:
// eventBus.js
import Vue from 'vue';
export const EventBus = new Vue();
他俩的父组件:
<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存储于事件总线中,而事件总线扮演的是桥梁,只是建立沟通。但是如果项目过大,有多个传递,后期维护起来还是比较难受。
用于父子、祖孙组件之间的通信,好处在于 不用一层一层的传递数据
provide
和inject
是Vue提供的两个钩子,和data、methods是同级(意思是data、methods在Vue里怎么写,这两个就怎么写)的,并且 provide 的书写形式和data一样
父组件中:
provide() {
return {
num: this.num
}
}
子组件:
inject: ['num'];
// 还有另外一种写法,可以访问父组件中的所有属性
provide() {
return {
app: this
};
}
data() {
return {
num: 1
};
}
inject: ['app'];
console.log(this.app.num);
**注意:**依赖注入 所提供的的属性是 非响应式的(非响应式属性的值发生改变不会触发视图更新);
子组件中:
<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.$parent
和this.$children
)
注意:
场景:A是B组件的父组件,B是C的父组件;A(爷) > B(父) > C(孙),隔代传递数据用什么好
方案:
$attrs: 继承所有的父组件属性(除了props传递的属性、class和style),一般用在子组件的子元素上;
l i s t e n e r s : 该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合 ‘ v − o n = " listeners: 该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合`v-on=" listeners:该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合‘v−on="listeners"` 将所有的事件监听器指向这个组件的某个特定的子元素(相当于子组件继承父组件的事件)。
inheritAttrs 属性:
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>
其实不同组件之间的通信只有三种类型(也是面试中最常见的问题):父子组件间通信、兄弟组件间通信、非父子组件间通信
子组件通过props属性来接收父组件的数据,然后父组件在子组件注册监听事件,子组件通过 emit 触发事件来向父组件发送数据;
通过 ref 属性给予组件设置一个名字,父组件通过 $refs 组件名获取子组件,子组件通过 $parent 获得父组件;
使用 provide/inject ,在父组件中通过provide 提供变量,在子组件中通过 inject 来将变量注入到组件中,这是一个不需要一层一层调用的方法;