在开发中,我们构建了组件树之后,除了父子组件之间的通信之外,还会有非父子组件之间的通信。这里主要讲两种方式:
- Provide/Inject
- Mitt全局事件总线
1、Provide和Inject
应用场景
比如有一些深度嵌套的组件,子组件想要获取父组件的部分内容,在这种情况下,如果我们仍然将props沿着组件链逐级传递下去,就会非常的麻烦。
对于这种情况下,我们可以使用 Provide和Inject,它可以实现非父子组件之间共享数据。
特点
- 无论层级结构有多深,父组件都可以作为其所有子组件(包括子孙)的依赖提供者。
- 父组件有一个provide选项来提供数据。
- 子组件有一个 inject 选项来使用数据。
实际上,你可以将依赖注入看作是“
long range props”(作用域绵长的props)。
父组件不需要知道哪些子组件使用它 provide 的 property;子组件不需要知道 inject 的 property 来自哪里。一般用的不多,提供一些主题、样式之类的。
基本使用
我们编写如下一个组件结构:
注:不能在定义provide属性的当前组件使用provide提供的数据,兄弟组件也不行。
踩坑
上图中的this指向谁?能不能访问到data里names的长度?为什么?
tips:在这script标签包裹的一段export代码属于一个模块作用域,此时this为undefined。
解决方法是改写provide为函数形式并返回一个对象(像组件里data一样)。
处理响应式数据
另外如果我们修改了this.names的内容,那么使用length的子组件会不会也发生相应的改变呢?
答案是否定的,因为length不是响应式数据(类似值拷贝)。 所以我们可以使用一些响应式的一些API来完成这些功能,比如说computed函数(vue3语法)。
注:因为 computed返回的是一个ref对象 ,需要手动通过.value形式获取其中真正的值 。
全局事件总线
Vue3从实例中移除了 $on、$off 和 $once 方法,所以如果希望继续使用全局事件总线,要通过第三方的库。Vue3官方有推荐一些库,例如 mitt 或 tiny-emitter
,
这里主要介绍一下
mitt库
的使用;
安装
npm install mitt
基本使用
封装一个工具类
eventbus.js:
import mitt from 'mitt';
const emitter = mitt();
// 可以创建多个
// export const emitter1 = mitt();
// export const emitter2 = mitt();
// export const emitter3 = mitt();
export default emitter;
在Home.vue中监听事件
在App.vue中触发事件
个人感觉这个api和socket很相像。
关闭监听
在某些情况下我们可能希望
取消掉之前注册的函数监听。