造輪子-tab欄切換以及$event和eventBus深入理解

1. 項目初始:

新建tabs.vue/tabs-body.vue/tabs-head.vue/tabs-item.vue/tabs-pane.vue

樣式結構
tabs.vue

<template>
    <div class="tabs">
        <slot></slot>
    </div>
</template>
<script>
    export default {
        name: 'GuluTabs',
        props: {
            selected: {
                type: String
            }
        },
        created(){
            
        }
    }
</script>
<style lang="scss" scoped>
    .tabs{
        
    }
</style>

app.js

new Vue({
  el: '#app',
  data: {
    selectedTab: 'sports'
  }
})

index.html

<div id="app">
   <!--selected就是默認一開始選中的tab項厘肮,當你點擊每個tab的時候需要更改selected的值,也就是更改selectedTab橡疼,這里需要使用@update:selected="selectedTab = $event"譬猫,使用vue語法糖聋亡,可以寫成下面的-->
    <g-tabs :selected.sync="selectedTab">
   <!--等價于<g-tabs :selected="selectedTab" @update:selected = "selectedTab = $event">-->
        <g-tabs-head>
            <g-tabs-item name="woman">
                美女
            </g-tabs-item>
            <g-tabs-item name="finance">
                財經(jīng)
            </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">
                財經(jīng)相關資訊
            </g-tabs-pane>
            <g-tabs-pane name="sports">
                體育相關資訊
            </g-tabs-pane>
        </g-tabs-body>
    </g-tabs>
</div>

相關補充:

$event是指對應的事件信息茅郎。
對于原生元素(如 button泛源、input)來說篙挽,$event 是原始的 DOM 事件冲秽。
對于自定義組件(如 child)來說舍咖,$event 是其自身$emit里的第二個參數(shù)。

上面的$event就是$emit的第二個參數(shù)锉桑,也就相當于$emit('update:selceted', 'xxx')排霉,也就是update:selected事件觸發(fā)時你寫的第二個參數(shù)就比如我這里的'xxx',那么你的$event就是'xxx'民轴,其實就是子組件給父組件傳遞的那個數(shù)據(jù)就相當于
index.html

<g-tabs @update:selected="add">

</g-tabs>
<g-tabs @update:selected="selectedTab=$event">
{{selectedTab}}//這里的值就是$event也就是你下面觸發(fā)update:selected事件傳入的數(shù)據(jù)'xxx'
</g-tabs>
<script>
var app = new Vue({
  data: {
    selectedTab: ''
  },
  methods: {
    //這里的value就是子組件傳遞來的數(shù)據(jù)攻柠,也就是$event,也就是當你在子組件觸發(fā)父組件的事件的時候后裸,$event就是你在子組件觸發(fā)事件時傳入的數(shù)據(jù)(也就是第二個參數(shù))
    add(value){
      console.log(value)//xxx
    }
  }
})
</script>

子組件tabs.vue

<template>
    <div class="tabs">
        <slot></slot>
    </div>
</template>
<script>
    export default {
        name: 'GuluTabs',
        created(){
            this.$emit('update:selected', 'xxx')
        }
    }
</script>

實現(xiàn)tab切換

通過事件中心發(fā)布訂閱給所有的組件瑰钮,誰變了就發(fā)布一個事件,其他的組件跟著訂閱就好
在tabs.vue組件中聲明一個事件中心

import Vue from 'vue'
    export default {
        name: 'GuluTabs',
        data(){
            return {
                eventBus: new Vue()
            }
        },
        provide(){
            return {
                eventBus: this.eventBus
            }
        },
        created(){
            console.log(this.eventBus)
            //this.$emit('update:selected', $event.tagrget)
        
        }
    }

在data里聲明這個eventBus微驶,然后通過provide(https://cn.vuejs.org/v2/api/#provide-inject)方法將當前的eventBus傳給eventBus好讓其他的組件都可以使用浪谴,然后哦當前組件只需要通過this.eventBus就可以使用开睡,這里通過proviede方法提供的屬性,在其他任何子孫組件中可以直接通過inject['eventBus']注入苟耻,然后只要在其他組件中使用this.eventBus就可以拿到這個事件中心了篇恒,然后只需要在每個組件的事件中心里觸發(fā)你的update:selcected事件,將它們的name就可以拿到它們對應的name了
在tabs-item和tabs-pane組件中分別監(jiān)聽事件中心的update:selected事件凶杖,當點擊tabs-item的時候觸發(fā)這個事件胁艰,并把當前name傳給組件
tabs-item.vue

<template>
    <div class="tabs-item" @click="xxx">
        <slot></slot>
    </div>
</template>
<script>
    export default {
        name: 'GuluItem',
        props: {
            disabled: {
                type: Boolean,
                default: false
            },
            name: {
                type: String | Number,
                required: true
            }
        },
        inject: ['eventBus'],
        created(){
            this.eventBus.$on('update:selected',(name)=>{
                console.log(name)
            })
        },
        methods: {
            xxx(){
                this.eventBus.$emit('update:selected',this.name)
            }
        }
    }
</script>

tabs-pane.vue

<template>
    <div class="tabs-pane">
        <slot></slot>
    </div>
</template>
<script>
    export default {
        name: 'GuluPane',
        inject: ['eventBus'],
        created(){
            this.eventBus.$on('update:selected',(name)=>{
                console.log(name)
            })
        }
    }
</script>

然后點擊tabs-item就會打印出6個對應的name,上面三個item和下面三個pane的


  • 問題1:我通過事件中心觸發(fā)了update:selected事件智蝠,那會不會觸發(fā)下面的yyy腾么?
    index.html
<g-tabs @update:selected="yyy">
</g-tabs>
<script>
  var app = new Vue({
    methods: {
      yyy(value){
        console.log(value)
      }
    }
  })
</script>

答:不會。因為你的事件中心eventBus是在tabs.vue里的data里聲明的一個new Vue()杈湾,而你上面的代碼<g-tabs @update:selected="yyy">是監(jiān)聽的<g-tabs>解虱,也就是組件tabs,當tabs觸發(fā)update:selected事件時毛秘,他才會執(zhí)行yyy饭寺,要想觸發(fā)tabs組件的update:selected事件阻课,就要直接在tabs.vue里寫

created(){
  this.$emit('update:selected', '這是this $emit出來的數(shù)據(jù)')
}

然后yyy就會執(zhí)行叫挟,就會打印出這是this $emit出來的數(shù)據(jù)

  • 問題2:我如果在<g-tabs-head>里觸發(fā)update:selected事件,那么<g-tabs>里的yyy會被觸發(fā)嗎限煞?
    index.html
<g-tabs @update:selected="yyy">
  <g-tabs-head>
  <g-tabs-head>
</g-tabs>

tabs-head

created(){
  this.$emit('update:selected','這是tabs-head拋出的數(shù)據(jù)')
}

答:不會抹恳。原因vue的事件是不會冒泡的,你在哪個組件上觸發(fā)事件署驻,就要對應的在哪個組件對應的標簽上監(jiān)聽事件奋献。

切換功能實現(xiàn)

在tabs.vue里觸發(fā)事件中心將當前的selected傳給組件

<template>
    <div class="tabs">
        <slot></slot>
    </div>
</template>
<script>
    import Vue from 'vue'
    export default {
        name: 'GuluTabs',
        props: {
            selected: {
                type: String,
                required: true
            },
            direction: {
                type: String,
                default: 'horizontal',
                validator(value){
                    return ['horizontal', 'vertical'].indexOf(value) >=0
                }
            }
        },
        data(){
            return {
                eventBus: new Vue()
            }
        },
        provide(){
            return {
                eventBus: this.eventBus
            }
        },
        mounted(){
            //用來初始化的時候
            this.eventBus.$emit('update:selected', this.selected)
        }
    }
</script>

然后在item和pane里監(jiān)聽事件中心,拿到事件觸發(fā)時傳入的selceted旺上,如果當前的name等于傳進來的selecetd就讓active為true
tabs-item.vue

<template>
    <div class="tabs-item" @click="xxx" :class="{active:active}">
        <slot></slot>
    </div>
</template>
<script>
    export default {
        name: 'GuluItem',
        props: {
            disabled: {
                type: Boolean,
                default: false
            },
            name: {
                type: String | Number,
                required: true
            }
        },
        data(){
            return {
                active: false
            }
        },
        inject: ['eventBus'],
        created(){
            this.eventBus.$on('update:selected',(name)=>{
                if(this.name === name){
                    this.active = true
                }else{
                    this.active = false
                }
            })
        },
        methods: {
          //每次點擊對應的tab將當前的name傳給事件
            xxx(){
                this.eventBus.$emit('update:selected',this.name)
            }
        }
    }
</script>
<style lang="scss" scoped>
    .tabs-item {
        flex-shrink: 0;
        padding: 0 2em;
        &.active{
            background: red;
        }
    }
</style>

tabs-pane.vue

<template>
    <div class="tabs-pane" v-show="active" :class="{active:active}">
        <slot></slot>
    </div>
</template>
<script>
    export default {
        name: 'GuluPane',
        inject: ['eventBus'],
        data(){
            return {
                active: false
            }
        },
        props: {
            name: {
                type: String | Number,
                required: true
            }
        },
        created(){
            this.eventBus.$on('update:selected',(name)=>{
                 if(this.name === name){
                    this.active = true
                }else{
                    this.active = false
                }
            })
        }
    }
</script>
<style lang="scss" scoped>
    .tabs-pane{
        &.active{
            background: red;
        }
    }
</style>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末瓶蚂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子宣吱,更是在濱河造成了極大的恐慌窃这,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件征候,死亡現(xiàn)場離奇詭異杭攻,居然都是意外死亡,警方通過查閱死者的電腦和手機疤坝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門兆解,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人跑揉,你說我怎么就攤上這事锅睛。” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵现拒,是天一觀的道長乖订。 經(jīng)常有香客問我,道長具练,這世上最難降的妖魔是什么乍构? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮扛点,結果婚禮上哥遮,老公的妹妹穿的比我還像新娘。我一直安慰自己陵究,他們只是感情好眠饮,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铜邮,像睡著了一般仪召。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上松蒜,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天扔茅,我揣著相機與錄音,去河邊找鬼秸苗。 笑死召娜,一個胖子當著我的面吹牛,可吹牛的內容都是我干的惊楼。 我是一名探鬼主播玖瘸,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼檀咙!你這毒婦竟也來了雅倒?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弧可,失蹤者是張志新(化名)和其女友劉穎蔑匣,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侣诺,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡殖演,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了年鸳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趴久。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖搔确,靈堂內的尸體忽然破棺而出彼棍,到底是詐尸還是另有隱情灭忠,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布座硕,位于F島的核電站弛作,受9級特大地震影響,放射性物質發(fā)生泄漏华匾。R本人自食惡果不足惜映琳,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜘拉。 院中可真熱鬧昔驱,春花似錦疗隶、人聲如沸茉继。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽持寄。三九已至源梭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稍味,已是汗流浹背废麻。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仲闽,地道東北人脑溢。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像赖欣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子验庙,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內容