vue-自定義組件&自定義事件&組件中的插槽

一、自定義組件

1、自定義組件的意義

當(dāng)一個(gè)布局或模塊需要在很多地方使用時(shí),我們就會(huì)對(duì)這個(gè)布局的代碼和樣式進(jìn)行多次復(fù)制粘貼,所以選擇對(duì)這個(gè)布局進(jìn)行封裝可以方便我們?cè)诠ぷ髦卸啻问褂谩?/p>

2、組件名

定義組件名的方式:
① 短橫線分隔命名(kebab-case)

Vue.component('my-component-name', { /* ... */ })

當(dāng)使用 kebab-case (短橫線分隔命名) 定義一個(gè)組件時(shí),你也必須在引用這個(gè)自定義元素時(shí)使用 kebab-case,例如 <my-component-name>。
注意:使用短橫線分隔命名需要使用單引號(hào)引起來(lái)
② 首字母大寫命名(PascalCase)

Vue.component('MyComponentName', { /* ... */ })

當(dāng)使用 PascalCase (首字母大寫命名) 定義一個(gè)組件時(shí),你在引用這個(gè)自定義元素時(shí)兩種命名法都可以使用。也就是說(shuō) <my-component-name> 和 <MyComponentName> 都是可接受的

3、局部組件(components{ })

局部注冊(cè)的組件,只能在當(dāng)前Vue實(shí)例中使用,并且在其子組件中不可用。
在components選項(xiàng)中定義局部組件。每個(gè)組件就是一個(gè)小型的Vue實(shí)例,它里面除了不能設(shè)置el選項(xiàng),其他選項(xiàng)它都有。
組件名稱:自定義,可以使用駝峰命名方式或者短橫線的命名方式,但是需要注意的是如果應(yīng)用到DOM中,就只能用短橫線命名的方式,否則就會(huì)報(bào)錯(cuò)。注意組件的名稱不要跟原生html元素重名。
template選項(xiàng):定義組件的模板。模板中必須包含一個(gè)根標(biāo)簽。
props選項(xiàng):定義組件標(biāo)簽上的屬性。駝峰命名法的 prop 名(postTitle)需要使用其等價(jià)的短橫線分隔命名法(post-title)命名。注意:props是只讀的,不能修改(解決辦法:在data中對(duì)props接收到的數(shù)據(jù)進(jìn)行中轉(zhuǎn))。

  • props的值可以是一個(gè)字符串?dāng)?shù)組,里面定義每一個(gè)標(biāo)簽屬性名稱,這是簡(jiǎn)單用法,不能對(duì)屬性做嚴(yán)格的驗(yàn)證。例如:props:["count"]。
  • props的值可以是一個(gè)對(duì)象,里面定義每個(gè)標(biāo)簽屬性名稱,以及對(duì)應(yīng)的類型。例如:props:{ count:Number }。
  • props的值可以是一個(gè)對(duì)象,里面定義的每個(gè)標(biāo)簽屬性名稱也可以是一個(gè)對(duì)象,在這個(gè)對(duì)象里面定義該屬性的完整信息。type 定義類型,required 非空,default 默認(rèn)值。例如:props: {count: { type: Number, required: true, default: 1 } }
    data:定義組件的數(shù)據(jù)。注意:Vue實(shí)例的data選項(xiàng)可以是一個(gè)對(duì)象,也可以是一個(gè)方法,由該方法返回一個(gè)對(duì)象。 但是在組件中,data選項(xiàng)必須是一個(gè)方法,由該方法返回一個(gè)對(duì)象。因?yàn)榻M件可能會(huì)使用很多次,如果data選項(xiàng)是對(duì)象的話,會(huì)導(dǎo)致多個(gè)組件使用了同一份數(shù)據(jù)。
new Vue({
  el: '#app',
  components: {
       // 自定義組件名
       'component-name': {
         template:`
         <div>
             <p> hello </p>
         </div>
         `,
         props:{ },
         data() {
             return { }
         }       
       }
  }
})
<div id="app">
     <component-name> </component-name>
</div>

使用示例 counter組件

    <div id="app">
        <ul class="list">
            <li v-for="(item,index) in list" :key="index">{{item.label}}--{{item.count}}</li>
        </ul>
        <b-counter v-for="(item,index) in list" :key="index" :label="item.label" :count="item.count"
            @synccount="synccount(index,$event)"></b-counter>
    </div>
        new Vue({
            el: '#app',
            // 定義數(shù)據(jù)
            data: {
                list: [
                    {  label: '衣服',count: 5  },
                    {  label: '褲子',count: 8  },
                    {  label: '鞋子',count: 3  },
                    {  label: '襪子',count: 10  }
                ]
            },
            methods: {
                synccount(index, e) {
                    // 同步更新商品的數(shù)量
                    this.list[index].count = e
                }
            },
            // 注冊(cè)組件
            components: {
                'b-counter': {
                    template: `
                    <div class="counter">
                        <div class="label">{{label}}</div>
                        <div class="btns">
                            <button @click="myCount--" :disabled="myCount===minCount">-</button>
                            <input readonly class="text" type="text" :value="myCount">
                            <button @click="myCount++" :disabled="myCount===maxCount">+</button>
                        </div>
                    </div>
                    `,
                    //props選項(xiàng)
                    props: {
                        // 文本
                        label: {
                            type: String,
                            // 允許為空
                            required: false,
                        },
                        // 數(shù)量
                        count: {
                            type: Number,
                            // 非空
                            required: true
                        },
                        // 最大值
                        maxCount: {
                            type: Number,
                            default: 999
                        },
                        // 最小值
                        minCount: {
                            type: Number,
                            default: 1
                        }
                    },
                    //定義數(shù)據(jù)
                    data() {
                        return {
                            // 將props接收到的count,中轉(zhuǎn)給myCount
                            myCount: this.count
                        }
                    },
                    // 監(jiān)聽(tīng)器
                    watch: {
                        // 偵聽(tīng)myCount是否發(fā)生變化
                        myCount(val) {
                            // 觸發(fā)一個(gè)自定義事件synccount,將count的最新值作為事件對(duì)象傳出去
                            this.$emit('synccount', val)
                        }
                    }
                }
            }
        })

實(shí)現(xiàn)效果:


image.png

4、全局組件

全局注冊(cè)的組件可以用在任何新創(chuàng)建的 Vue 根實(shí)例 (new Vue) 的模板中。
使用Vue.component來(lái)創(chuàng)建全局組件。Vue.component的第一個(gè)參數(shù)就是組件名。

new Vue({
  el: '#app',
  components: {
       // 自定義組件名
       'component-name': {
         template:`
         <div>
             <p> hello </p>
         </div>
         `,
         props:{ },
         data() {
             return { }
         }       
       }
  }
})
<div id="app">
     <component-name> </component-name>
</div>

使用示例 評(píng)分組件

    <div id="app">
        <ul class="list">
            <li v-for="(item,index) in list" :key="index">{{item.title}}--{{item.value}}</li>
        </ul>
        <b-star v-for="(item,index) in list" :key="index" 
        :title="item.title" :value="item.value" @syncvalue="syncvalue(index,$event)"></b-star>
    </div>
        // 注冊(cè)全局組件
        Vue.component('b-star', {
            //模板
            template:`
            <div class="star">
                <div class="title">{{title}}</div>
                <div>
                    <i v-for="item in 10" :key="item" class="iconfont" 
                    :class="item<=myValue?'icon-xingxing':'icon-star'"
                    @mouseenter="enter(item)" @mouseleave="leave(item)" @click="okValue=item"></i>
                </div>
            </div>
            `,
            // 定義組件的標(biāo)簽屬性
            props:{
                title:{
                    type:String,
                    required:false
                },
                value:{
                    type:Number,
                    default:0
                }
            },
            // 數(shù)據(jù)
            data() {
                return {
                    // 中轉(zhuǎn)props傳進(jìn)來(lái)的value值
                    myValue:this.value,
                    // 定義一個(gè)確定值
                    okValue:this.value
                }
            },
            // 方法
            methods: {
               // 鼠標(biāo)進(jìn)入時(shí),調(diào)用的方法
                enter(val){
                    this.myValue = val
                },
               // 鼠標(biāo)離開(kāi)時(shí),調(diào)用的方法
                leave(val){
                    this.myValue = this.okValue
                }
            },
            // 監(jiān)聽(tīng)器
            watch:{
                // 偵聽(tīng)okValue的變化
                okValue(val){
                    // 觸發(fā)自定義事件
                    this.$emit('syncvalue',val)
                }
            }
        })
        new Vue({
            el:'#app',
            data:{
                list:[
                    {
                        title:'產(chǎn)品質(zhì)量',
                        value:5
                    },
                    {
                        title:'物流速度',
                        value:7
                    },
                    {
                        title:'客服態(tài)度',
                        value:2
                    }
                ]
            },
            methods: {
                // 同步評(píng)分
                syncvalue(index,e){
                    this.list[index].value = e
                }
            },
        })

實(shí)現(xiàn)效果:


image.png

二、自定義時(shí)間$emit

$emit()用于觸發(fā)自定義事件。注意:事件名稱中不能采用大寫字母。

// 觸發(fā)一個(gè)自定義事件,事件名稱是synccount,將val作為事件對(duì)象傳出去
this.$emit('synccount',val)
<component-name @synccount="synccount(doSomething)"> </component-name>

三、組件中的插槽

slot 用于在組件的內(nèi)部定義插槽,組件標(biāo)簽之間的所有html內(nèi)容,會(huì)在插槽所在位置呈現(xiàn)。

<div id="app">
        <b-tab :list="list" :active="activeIndex">
            <h2>全國(guó)著名小吃</h2>
        </b-tab>
    </div>
        Vue.component('b-tab', {
            template: `
            <div class="tab">
                <slot></slot>
                <ul class="titles">
                    <li @click="activeIndex=index" :class="{active:activeIndex===index}" v-for="(item,index) in list" :key="index">{{item.title}}</li>
                </ul>
                <ul class="contents">
                    <li v-show="activeIndex===index" v-for="(item,index) in list" :key="index">{{item.content}}</li>
                </ul>
            </div>
            `,
            props: ['list', 'active'],
            data() {
                return {
                    activeIndex: this.active
                }
            }
        })
        new Vue({
            el: '#app',
            data: {
                // 高亮索引
                activeIndex: 0,
                list: [
                    {
                        title: '北京',
                        content: '北京的糖葫蘆真好吃'
                    },
                    {
                        title: '南京',
                        content: '南京的鹽水鴨真好吃'
                    },
                    {
                        title: '武漢',
                        content: '武漢的熱干面真好吃'
                    },
                    {
                        title: '長(zhǎng)沙',
                        content: '長(zhǎng)沙的臭豆腐真好吃'
                    }
                ]
            }
        })

實(shí)現(xiàn)效果:


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容