VUE從入門到入坑—07.深入理解v-model / 插槽|具名插槽|作用域插槽 / .sync修飾符 / mixin混入


上篇:VUE從入門到入坑—06. 父子組件通信 / 幾種常用的第三方組件庫(kù)

一、深入理解v-model

1.v-model,可以實(shí)現(xiàn)數(shù)據(jù)雙向的綁定,其實(shí)就是一個(gè)由屬性和事件組成的語(yǔ)法糖。

元素類型 屬性 事件
input[type=text]、textarea value input
input[checkbox]、input[radio] checked change
select value change

2.將自定義組件,綁定數(shù)據(jù)的屬性改成value,監(jiān)聽事件的名稱改成input,就可以使用v-model簡(jiǎn)寫形式。

<div id="app">
    <ul class="list">
        <li>{{yf.label}}--{{yf.count}}</li>
        <li>{{kz.label}}--{{kz.count}}</li>
    </ul>
    <b-counter :label="yf.label" :value="yf.count" @input="yf.count=$event"></b-counter>
    <b-counter :label="kz.label" v-model="kz.count"></b-counter>
</div>
Vue.component('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: {
        //文本
        label: {
            type: String,
            required: false,
        },
        //數(shù)量
        value: {
            type: Number,
            required: true
        },
        //最大值
        maxCount: {
            type: Number,
            default: 999
        },
        //最小值
        minCount: {
            type: Number,
            default: 1
        }
    },
    //定義數(shù)據(jù)
    data() {
        return {
            myCount: this.value
        }
    },
    //監(jiān)聽器
    watch: {
        myCount(val) {
            // 觸發(fā)一個(gè)自定義事件,事件名稱是input
            this.$emit('input', val)
        }
    }
})

let vm = new Vue({
    el: '#app',
    data: {
        // 衣服
        yf: {
            label: '衣服',
            count: 5
        },
        // 褲子
        kz: {
            label: '褲子',
            count: 5
        }
    }
})

效果:

二、插槽

1.組件中的插槽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)沙的臭豆腐真好吃'
                    }
                ]
            }
        })

效果:

2.具名插槽,在組件內(nèi)部通過(guò)slot標(biāo)簽定義插槽,再通過(guò)name屬性給插槽定義名字,這樣的插槽稱之為具名插槽。插槽的默認(rèn)名稱是:default。如果有多個(gè)插槽,允許其中一個(gè)插槽不定義名稱。在template標(biāo)簽中采用v-slot:插槽名稱的方式,指定內(nèi)容在哪一個(gè)具體的插槽中呈現(xiàn)。#是v-slot:的簡(jiǎn)寫。

<div id="app">
    <b-box>
        <!-- 在template組件中采用v-slot:插槽名稱的方式,指定使用哪個(gè)插槽 -->
        <template v-slot:house>
            <div>有5套房子</div>
        </template>
        <!-- #是v-slot:的簡(jiǎn)寫 -->
        <template #car>
            <div>有8輛汽車</div>
        </template>
        <template v-slot:money>
            <div>有3千萬(wàn)存款</div>
        </template>
    </b-box>
</div>
Vue.component('b-box', {
    template: `
        <div class="box">
            <div class="item">
                <h2>房產(chǎn)信息</h2>
                <slot name="house"></slot>
            </div>
            <div class="item">
                <h2>車輛信息</h2>
                <slot name="car"></slot>
            </div>
            <div class="item">
                <h2>存款信息</h2>
                <slot name="money"></slot>
            </div>
        </div>
    `
})
new Vue({
    el: '#app',
})

3.作用域插槽,必須是具名插槽;在作用域插槽上,可以通過(guò)v-bind:綁定屬性,綁定的屬性通過(guò)指定的作用域變量(通常會(huì)定義scope變量,變量名隨意?。┤ソ邮?。

<div id="app">
    <b-box>
        <template v-slot:list="scope">
            <button @click="priceDown(scope.list,scope.index)">降價(jià)</button>
            <button @click="priceUp(scope.list,scope.index)">加價(jià)</button>
            <button @click="scope.list.splice(scope.index,1)">刪除</button>
        </template>
    </b-box>
</div>
Vue.component('b-box', {
    template:`
    <div>
        <ul>
            <li v-for="(item,index) in list" :key="index">
                <span>{{item.id}}-{{item.name}}-{{item.price}}</span>
                <slot name="list" v-bind:index="index" v-bind:list="list"></slot>
            </li>
        </ul>
    </div>
    `,
    data() {
        return {
            list:[
                {
                    id:1001,
                    name:'蘋果手機(jī)',
                    price:5999
                },
                {
                    id:1002,
                    name:'華為手機(jī)',
                    price:6999
                },
                {
                    id:1003,
                    name:'小米手機(jī)',
                    price:7999
                },
                {
                    id:1004,
                    name:'三星手機(jī)',
                    price:8999
                }
            ]
        }
    }
})
new Vue({
    el:'#app',
    methods: {
        priceDown(list,index){
            list[index].price-=1000
        },
        priceUp(list,index){
            list[index].price+=1000
        }
    },
})

三、.sync修飾符

1.綁定屬性采用:xx.sync修飾符,可以省略u(píng)pdate:xx對(duì)應(yīng)的事件綁定。
2.必要條件①:屬性綁定必須是xx.sync;必要條件②:自定義事件必須是update:xx。
3.如果觸發(fā)的事件名稱是update:屬性名,那么就可以使用.sync修飾符簡(jiǎn)化調(diào)用的過(guò)程。
4.總結(jié):如果組件只回傳一份數(shù)據(jù),用v-model。如果組件回傳多份數(shù)據(jù),用.sync修飾符。

<div id="app">
    <div>衣服:{{yf}},褲子:{{kz}},鞋子:{{xz}}</div>
    <!-- 屬性綁定必須是:xx.sync -->
    <!-- :yf.sync="yf" @update:yf="yf=$event"可以簡(jiǎn)寫成 :yf.sync="yf" -->
    <b-counter :yf.sync="yf" @update:yf="yf=$event" :kz.sync="kz" :xz.sync="xz"></b-counter>
</div>
Vue.component('b-counter', {
    template: `
        <div>
            <div class="counter">
                <div class="label">衣服:</div>
                <div class="btns">
                    <button @click="yfCount--">-</button>
                    <input readonly class="text" type="text" :value="yfCount">
                    <button @click="yfCount++">+</button>
                </div>
            </div>
            <div class="counter">
                <div class="label">褲子:</div>
                <div class="btns">
                    <button @click="kzCount--">-</button>
                    <input readonly class="text" type="text" :value="kzCount">
                    <button @click="kzCount++">+</button>
                </div>
            </div>
            <div class="counter">
                <div class="label">鞋子:</div>
                <div class="btns">
                    <button @click="xzCount--">-</button>
                    <input readonly class="text" type="text" :value="xzCount">
                    <button @click="xzCount++">+</button>
                </div>
            </div>
        </div>
    `,
    props: ['yf', 'kz', 'xz'],
    data() {
        return {
            yfCount: this.yf,
            kzCount: this.kz,
            xzCount: this.xz
        }
    },
    watch:{
        yfCount(val){
            // 自定義事件必須是update:xx
            this.$emit('update:yf',val)
        },
        kzCount(val){
            this.$emit('update:kz',val)
        },
        xzCount(val){
            this.$emit('update:xz',val)
        }
    }
})
new Vue({
    el: '#app',
    data: {
        //衣服數(shù)量
        yf: 5,
        //褲子數(shù)量
        kz: 5,
        //鞋子數(shù)量
        xz: 5
    }
})

四、mixin混入

1.混入 (mixin),提供了一種非常靈活的方式,來(lái)分發(fā) Vue 組件中的可復(fù)用功能。mixin()方法的參數(shù)是配置對(duì)象,Vue實(shí)例可以配置的東西,它都可以配置。比如:數(shù)據(jù),方法,生命周期鉤子函數(shù),計(jì)算屬性,偵聽器,過(guò)濾器,等等。

2.全局混入的內(nèi)容,之后創(chuàng)建的所有Vue實(shí)例包括組件實(shí)例都將擁有。在創(chuàng)建Vue實(shí)例時(shí),會(huì)將mixin里面的成員跟Vue實(shí)例自身的成員進(jìn)行合并,如果沖突了,最終采用Vue實(shí)例身上的成員。

特別注意:生命周期鉤子不是合并,是疊加執(zhí)行,是先執(zhí)行mixin里面的生命周期鉤子,再執(zhí)行Vue實(shí)例里面的生命周期鉤子。

<div id="app1">
    <p>姓名:<input type="text" v-model="name"></p>
    <p>年齡:<input type="text" v-model.number="age"><button @click="age++">++</button></p>
    <p>性別:<input type="text" v-model="sex"></p>
    <p>稅前薪資:<input type="text" v-model="salary">稅后薪資:<input type="text" :value="salary2"></p>
    <button @click='sayHi'>sayHi</button>
    <div>汽車信息:{{car}}</div>
</div>
<hr>
<div id="app2">
    <p>姓名:<input type="text" v-model="name"></p>
    <p>年齡:<input type="text" v-model.number="age"><button @click="age++">++</button></p>
    <p>性別:<input type="text" v-model="sex"></p>
    <p>稅前薪資:<input type="text" v-model="salary">稅后薪資:<input type="text" :value="salary2"></p>
    <button @click='sayHi'>sayHi</button>
    <div>飛機(jī)信息:{{plane}}</div>
</div>
//全局混入,給所有的Vue實(shí)例混入統(tǒng)一的成員 -- 注意:必須要先執(zhí)行
Vue.mixin({
    data() {
        return {
            name: '',
            age: 0,
            sex: '男',
            salary: 1000
        }
    },
    computed: {
        salary2() {
            return this.salary * 0.88
        }
    },
    methods: {
        sayHi() {
            alert(`大家好!我叫${this.name},性別是${this.sex},今年${this.age}歲`)
        }
    },
    watch: {
        age(val) {
            if (val > 100) {
                alert('年齡不能超過(guò)100歲')
                this.age = 100
            }
        }
    },
    mounted() {
        console.log('mixin:組件掛載完成');
    },
})
// 創(chuàng)建第一個(gè)Vue的實(shí)例--操作的容器是#app1
new Vue({
    el: '#app1',
    data() {
        return {
            car: {
                name: '奔馳',
                price: '100W'
            }
        }
    },
    // 注意:先執(zhí)行混入的生命周期,再執(zhí)行自己的生命周期
    mounted() {
        console.log('app1:組件掛載完成');
    },
})
// 創(chuàng)建第二個(gè)Vue的實(shí)例--操作的容器是#app2
new Vue({
    el: '#app2',
    data() {
        return {
            plane: {
                name: '波音747',
                price: '100Y'
            }
        }
    },
})

效果:

五、混入ajax的基本操作

1.請(qǐng)求ajax相關(guān)的操作的時(shí)候也可以通過(guò)mixin混入給Vue。

<div id="app1">
    <button @click="getSubjects">請(qǐng)求課程數(shù)據(jù)</button>
    <div>
        {{subjects}}
    </div>
</div>
Vue.mixin({
    methods: {
        //get請(qǐng)求
        async $get(url, params) {
            let { data } = await axios.get(url, { params })
            return data
        },
        //post請(qǐng)求
        async $post(url, params) {
            let { data } = await axios.post(url, params)
            return data
        }
    }
})

new Vue({
    el: '#app1',
    data() {
        return {
            //課程數(shù)組
            subjects: []
        }
    },
    methods: {
        async getSubjects() {
            let { data } = await this.$get('http://www.bingjs.com:81/Subject/GetSubjectsConditionPages')
            this.subjects = data
        }
    }
})

Over!

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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