Vue面试之组件通信的方式总结
- $ref
- provide&inject
- provide
- inject
- EventBus事件总线
- vuex
最近在整理一些前端面试中经常被问到的问题,分为vue相关、react相关、js相关、react相关等等专题,可持续关注后续内容,会不断进行整理~
在Vue框架中,提供了多种组件通信方式:props父子组件传参、插槽传参、Event Bus方式、vuex方式传参等,之前已对前两种方式进行了总结,本文继续总结其余方法~
$ref
在Vue中很少会直接操作DOM,但是一般情况下会用ref和$ refs取某DOM结构,具体来说:利用ref给元素或子组件注册信息,则该信息就会被注册到父组件的$ refs对象上。
$refs是一个对象,包含已经注册过ref的所有子组件的内容
ref有三种用法:
- ref加在普通元素上,则通过this.$refs.xxx获取到的是dom元素;
<p id="p1" ref="p1">这是第一段</p>
console.log(this.$refs.p1) // 就可以拿到p1对应的dom元素
2. ref加在子组件上,则通过this.$refs.xxx获取到的是组件,可拿到子组件的所有属性和方法,实现子组件向父组件通信;同时如果在使用子组件方法时传入参数,则可以实现父组件向子组件通信;
上图展示了利用this.refs.xxx可以获取到子组件身上的所有属性和方法,从而实现子组件向父组件通信;
这里需要注意的一点是,$refs只有在子组件被渲染后才能拿到,使用时一定要确保子组件已经被渲染,否则拿到的只能是undefined
父组件利用子组件的submit方法,可在使用时传递参数,这样调用时,子组件就可以拿到传递过来的参数值,实现父组件向子组件的通信;
<!-- 父组件中 -->
<Son ref="son"></Son>
<button @click="finish">提交</button>
// 父组件中
finish() {
this.$refs.son.submit('12')
// 调用子组件中的submit方法,并将12作为参数传递
}
// 子组件中
submit(params) {
console.log('拿到了父组件传递过来的参数', params)
// 可以拿到父组件传递过来的参数12
}
- 如果使用 v-for 渲染多个相同类型的子组件,$refs 将成为一个数组。
provide&inject
provide和inject也可以用来进行跨层级组件通信,父组件通过provide提供数据或方法,而子孙组件通过inject拿到数据或方法;
provide
它有两种写法:
- 对象写法
如果传递的数据是个‘死数据’(静态数据),则可以直接利用对象形式将其传递;
// Parent.vue
export default {
// provide对象写法,直接将数据进行传递
provide: {
age: 13
},
};
- 函数写法
// Parent.vue
export default {
data() {
return {
list: [...], // 一些数据
};
},
provide() {
// provide函数写法,可以将动态数据传递
return {
list: this.list, // 提供属性
submit: this.submit // 提供方法
};
},
methods: {
submit() {
// 一些逻辑
}
}
};
inject
inject的写法也有两个:
- 数组写法
如果不用给传递过来的数据重命名,可采用此方法(上例中的传递age)
// Child.vue
export default {
inject: ['age'], // 数组形式
mounted() {
console.log(this.age); // 可以访问父组件提供的数据,12
},
};
- 对象形式
如果需要对传递过来的数据重命名,可采用对象形式(上例中传递submit与list)
// Child.vue
export default {
inject: {
parentList: 'list',
parentSubmit: 'submit'
},
mounted() {
console.log(this.parentList); // 可以访问父组件提供的数据
this.parentSubmit(); // 可以调用父组件提供的方法
},
};
需要注意的是,provide 提供的数据不是响应式的,这意味着如果提供的数据发生变化,子组件不会自动重新渲染。如果需要响应式数据,可以使用 Vue 的实例或 Vuex 等状态管理工具。
EventBus事件总线
事件总线是一种简单而有效的组件通信方式,它通过一个中央的事件总线来传递消息,允许不同组件之间进行解耦合的通信。它属于** 订阅/发布模式实现的一种方式,通常可以通过创建一个全局的Vue实例**作为事件总线。分为如下几个步骤:
- 创建事件总线实例
// eventBus.js
import Vue from 'vue';
export const EventBus = new Vue();
- 在父组件中发送事件,使用$emit
// 父组件
import { EventBus } from '@/utils/eventBus.js'
showDetails (val) {
EventBus.$emit('message-event', '12')
// 发送message-event事件,传递12这个数据
},
- 在子组件中接收事件,使用$on
// 子组件
import { EventBus } from '@/utils/eventBus.js'
mounted() {
EventBus.$on('message-event', this.doSomething)
// 当message-event发生改变时,就会触发doSomething方法
},
methods() {
doSomething(params) {
console.log('被事件触发了此方法~')
console.log('并能接收到了数据', params)
}
}
4. 为防止内存泄露,一般在组件卸载时需要解绑事件总线,使用$off
// 子组件
beforeDestroy() {
EventBus.$off('message-event', this.doSomething);
// 解绑message-event事件
},
在解绑事件时,$off 方法的第二个参数是一个可选的回调函数。如果在 $on 时使用了具名的回调函数,可以在 $off 时只传递回调函数而省略事件名称,这样就能够更精确地解绑特定的回调函数,如:
beforeDestroy() {
EventBus.$off(this.doSomething);
},
- 在大型应用中,为了防止命名冲突,可以考虑使用命名空间来管理事件总线。可以在 EventBus.js 中为每个模块或组件创建一个子实例,以确保事件名称的唯一性。
// EventBus.js
import Vue from 'vue';
export const ParentEventBus = new Vue();
export const ChildEventBus = new Vue();
// 父组件
import { ParentEventBus } from '@/utils/eventBus.js'
注意:
尽量避免滥用事件总线,特别是在大型应用中。对于父子组件通信或兄弟组件通信,更推荐使用 props 和 $emit;
在多人协作或大型项目中,可能会使用更为复杂的状态管理工具(例如 Vuex)来代替事件总线;
vuex
该方式在以往文章中已经进行了详细的讲述,请移步Vue2教程详解七(Vuex3)