最近的学习中接触到了在 Vue中的发布-订阅模式,在此,希望做个记录并简要谈谈自己的体会
1. 发布订阅模式主要包含哪些内容
- 发布函数,发布的时候执行相应的回调
- 订阅函数,添加订阅者,传入发布时要执行的函数,可能会携额外参数
- 一个缓存订阅者以及订阅者的回调函数的列表
- 取消订阅(需要分情况讨论)
为什么会使用发布订阅模式呢? 它的优点在于:
实现时间上的解耦(组件,模块之间的异步通讯); 对象之间的解耦,交由发布订阅的对象管理对象之间的耦合关系.
2. 举例
目标
希望用户如下使用代码:
<g-tabs>
<g-tabs-nav>
<g-tabs-item name="tab1"></g-tabs-item>
<g-tabs-item name="tab2"></g-tabs-item>
</g-tabs-nav>
<g-tabs-content>
<g-tabs-pane name="tab1"></g-tabs-pane>
<g-tabs-pane name="tab2"></g-tabs-pane>
</g-tabs-content>
</g-tabs>
思路分析
爷爷组件tabs 有数据 selectedTag, 然后会向head和Body传值,然后,head会向自己的三个孩子传值,body会向自己的三个孩子传值
这里我们可以用到provide和inject
provide 选项允许我们指定我们想要提供给后代组件的数据/方法
然后在任何后代组件里,我们都可以使用 inject 选项来接收指定的我们想要添加在这个实例上的 property.
由于爷爷、爸爸、孙子三者之间的信息传递较为复杂,我们选择用eventbus来减少复杂
用new Vue()做EventBus
如果一个对象可以on, $off就是一个eventBus
Tab.vue
@Component
export default class Tab extends Vue {
...
eventBus = new Vue();
@Provide('eventbus') eventbus: Vue = this.eventBus;
爷爷provide, 然后儿子、孙子里inject
@Component
export default class TabHead extends Vue {
@Inject() eventbus!: Vue;
created(){
console.log('爷爷给爸爸的eventBus')
console.log(this.eventbus)
}
}
@Component
export default class TabItem extends Vue {
@Inject() eventbus!: Vue;
@Prop(String) name: string | undefined;
created() {
console.log("爷爷给tab item的eventbus")
console.log(this.eventBus)
this.eventbus.$on('update:selected', (name: string) => {
console.log(name);
});
}
}
- 到底是谁在触发,谁在监听?
this.$emit 是当前实例在触发
this.eventBus.$emit 是eventBus这个对象本身在触发,注意两者的区别
在vue中,事件并不会冒泡,我们需要关注是在哪个对象上触发的事件
实现切换tag功能
- 第一步是从外界取值
点击一个tag后,爷爷组件会广而告之所有人,XXX被选中了,然后他的孙子们通过eventBus就能知道了!
然后我们根据传进来的变量,计算出css class, 然后更新样式
@Provide('eventbus') eventbus: Vue = this.eventBus;
mounted() {
this.eventbus.$emit('update:selected', this.selected);
}
TabPanel 和 TabItem
created() {
this.eventbus.$on('update:selected', (name: string) => {
this.active = name === this.name;
});
}
- 第二步是用点击事件从内部更新
因为vue里事件不会冒泡,所以我们监听点击元素:tab-item, 设置onClick事件
<template>
<div class="tab-item" @click="xxx" :class="classes">
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import {Component, Inject, Prop} from 'vue-property-decorator';
@Component
export default class TabItem extends Vue {
@Inject() eventbus!: Vue;
@Prop(String) name: string | undefined;
@Prop(Boolean) disabled = false;
active = false;
get classes() {
return {
active: this.active
};
}
created() {
this.eventbus.$on('update:selected', (name: string) => {
this.active = name === this.name;
});
}
xxx() {
this.eventbus.$emit('update:selected', this.name, this)
}
}
每次点击,就往事件中心广播 是哪个Name被点击了