前言
什么叫做发布订阅,简单来说就是爷爷、爸爸和孙子祖孙三代的通信
现在有个需求,做个tabs


思路如下所示:

首先会创建五个组件,它们分别是tabs(爷爷),head(大儿子),item(大儿子的儿子们),body(二儿子)和pane(二儿子的儿子们)
一开始item1 是亮着的,那么pane也应该是亮着的。倘若这时候要是点击了item2,怎么通知到pane2呢?
第一种方法是:一开始从爷爷这通知,看红色箭头,这时候点击ietm2,看蓝色箭头,通知head,关闭item1,在返回tabs改变selected=item2,在通知body,关闭pane1,并点亮pane2。(麻烦)
第二种方法就是 发爷爷、爸爸和孙子祖孙三代的通信 布订阅
另外再做一个变量,名为EventBus,当我点击了item2的时候,这时候通知EventBus,由EventBus通知tabs,更改selected=item2。点击item3,方法如上。
实例
index.html
<div id="app">
<!--<g-tabs :selected="selectedTab" @update:selected="selectedTab =$event">-->
<g-tabs :selected.sync="selectedTab" @update:selected="yyy">
<g-tabs-head>
<template slot="actions">
<button>设置</button>
</template>
<g-tabs-item name="woman">
<g-icon name="setting"></g-icon>
美女
</g-tabs-item>
<g-tabs-item name="finance" disabled>
财经
</g-tabs-item>
<g-tabs-item name="sports">
体育
</g-tabs-item>
</g-tabs-head>
<g-tabs-body>
<g-tabs-pane name="woman">
美女相关资讯
</g-tabs-pane>
<g-tabs-pane name="finance">
财经相关资讯
</g-tabs-pane>
<g-tabs-pane name="sports">
体育相关资讯
</g-tabs-pane>
</g-tabs-body>
</g-tabs>
</div>
tabs.vue
<template>
<div class="tabs">
<slot></slot>
</div>
</template>
<script>
import Vue from 'vue'
export default {
name: "GuLuTabs",
props: {
selected: {
type: String,
required: true, // 必须输入 selected 这个值,如果没有输入也会在控制台显式报错
},
direction: {
type: String,
default: 'horizontal',
validator(value) {
// return ['horizontal', 'vertical'].includes(value)
return ['horizontal', 'vertical'].indexOf(value) > -1;
}
}
},
data() {
return {
eventBus: new Vue
}
},
provide() {
// 事件中心,订阅发布
return {
eventBus: this.eventBus
}
},
mounted() {
// this.$emit('update:selected','这是一个 this.$emit 出来的数据');
this.eventBus.$emit('update:selected',this.selected);
// this.$emit('update:selected','xxx')
},
created() {
this.$emit('update:selected','这是一个 this.$emit 出来的数据');
this.eventBus.$emit('update:selected','this.eventBus.$emit 出来的数据');
// this.$emit('update:selected','xxx')
}
}
</script>
<style scoped lang="scss">
.tabs {
}
</style>
解析:
- 这里面的selected是选择哪个item,是必须要输入的值,这里面的要接收前端开发者传来的值,所以放在props中,另一点是required的含义是:
不允许为空,必须传值进来
- includes 和 indexof 的区别
includes 不支持IE 传的值是布尔型
indexof 支持IE 结果是数值 - data和props的区别
- provide()与inject
其余的子组件中必须要有 inject
开始的写法
<script>
data() {
return {
}
},
provide() {
// 事件中心,订阅发布
return {
eventBus: new Vue
}
},
created() {
console.log("EventBus");
this.$emit(this.eventBus);
console.log("this");
this.$emit(this);
},
</script>
在浏览器中运行后,发现打印出来的值没有eventBus,于是将new Vue
声明在 data(){}里面


body和head的写法
<template>
<div class="tabs-body">
<slot></slot>
</div>
</template>
<script>
export default {
name: "GuLuTabsBody",
inject: ['eventBus'],
created() {
}
}
</script>
<style scoped lang="scss">
.tabs-body {
}
</style>
----------------------------
<script>
export default {
name: "GuLuTabsHead",
inject: ['eventBus'],
created() {
}
}
</script>
item和pane的写法
<template>
<div class="tabs-item" @click="xxx" :class="classes">
<slot></slot>
</div>
</template>
<script>
export default {
name: "GuLuTabsItem",
inject: ['eventBus'], // 不需要用户传值,自身维护值
data() {
return {
active: false
}
},
props: {
disabled: {
type: Boolean,
default: false
},
name: {
type: String | Number,
required: true
}
},
computed:{
classes(){
return{
active:this.active
}
}
},
created() {
this.eventBus.$on('update:selected', (name) => {
this.active = name === this.name ? true : false;
});
},
methods: {
xxx() {
this.eventBus.$emit('update:selected', this.name)
}
}
}
</script>
<style scoped lang="scss">
.tabs-item {
flex-shrink: 0; /*指定了 flex 元素的收缩规则*/
padding: 0 1em; /*用em就是不关心隔了多少像素,就关心字和字之间是否隔得开*/
&.active{
background: red;
}
}
</style>
网友评论