通過(guò)一個(gè)例子講解Vue組合式API

一、Composition API概述

當(dāng)我們編寫(xiě)Vue組件時(shí)候,組件中可能包含一系列的功能,例如一個(gè)代碼倉(cāng)庫(kù)管理的應(yīng)用,用戶(hù)的倉(cāng)庫(kù)列表可以看做是一個(gè)組件,這個(gè)組件還包含篩選、搜索的功能。

所謂的功能我們可以理解為MVC中的Model和Controller。從視圖角度,組件是最基本的代碼復(fù)用單元,但是從邏輯上,功能模塊是最基本的代碼復(fù)用單元。

每個(gè)組件中可能包含多個(gè)功能(也稱(chēng)為關(guān)注點(diǎn)),而多個(gè)功能的代碼會(huì)分散在Vue組件的各個(gè)部分:data/props/watch/computed/dom event callback/lifescycle。

這會(huì)帶來(lái)兩個(gè)問(wèn)題:

  1. 可讀性和可維護(hù)性差,比如我們想要閱讀某個(gè)功能的邏輯,需要翻遍整個(gè)組件,到處查看相應(yīng)的邏輯;當(dāng)需要改動(dòng)某個(gè)功能時(shí)候,需要在很多地方進(jìn)行改動(dòng)。
  2. Vue未提供功能模塊級(jí)別的代碼復(fù)用支持,只提供組件級(jí)別的復(fù)用的話(huà),在大型項(xiàng)目中是不夠的。

為了解決上面兩個(gè)問(wèn)題,Vue需要提供功能模塊級(jí)別的代碼組織方式,即需要將同一個(gè)功能模塊的代碼寫(xiě)在同一片區(qū)域,而不是分散到組件的各個(gè)API中,另外還需要支持功能模塊的封裝,以便我們可以提取功能模塊的代碼進(jìn)行復(fù)用。

如何實(shí)現(xiàn)上述兩個(gè)能力呢?我們看看一個(gè)功能模塊都包含哪些內(nèi)容。

  1. 數(shù)據(jù)
  2. 數(shù)據(jù)的計(jì)算
  3. 數(shù)據(jù)的更新
  4. 數(shù)據(jù)監(jiān)聽(tīng)

其中數(shù)據(jù)部分的代碼寫(xiě)在Vue組件中的data、props中

數(shù)據(jù)計(jì)算部分的代碼寫(xiě)在Vue組件中的computed和處理數(shù)據(jù)的方法中

數(shù)據(jù)更新部分代碼寫(xiě)在生命周期鉤子方法和交互事件中

數(shù)據(jù)監(jiān)聽(tīng)代碼寫(xiě)在watch中

因此我們希望Vue能提供一個(gè)API,讓我們把一個(gè)功能模塊(即關(guān)注點(diǎn))的這些代碼(包括data/props/computed/watch/dom event callback/lifecycle)都寫(xiě)在一起。并且能將其提取。

組合式API就提供了這樣的能力。

總之,組合式API是在Vue3中新增加的API,它有兩點(diǎn)優(yōu)勢(shì):

  1. 讓功能模塊代碼(關(guān)注點(diǎn))聚合在一起,使可讀性和可維護(hù)性更高。
  2. 提供了一種功能模塊級(jí)別的復(fù)用代碼的方式。

二、Composition API使用

前端面試刷題網(wǎng)站靈題庫(kù),收集大廠(chǎng)面試真題,相關(guān)知識(shí)點(diǎn)詳細(xì)解析。

開(kāi)發(fā)者如何使用組合式API呢?簡(jiǎn)單地說(shuō),需要在Vue組件里實(shí)現(xiàn)setup方法,并在方法中返回一些方法和屬性,返回的所有內(nèi)容都將暴露給組件的其余部分 (計(jì)算屬性、方法、生命周期鉤子等等) 以及組件的模板。

首先看一個(gè)簡(jiǎn)單的例子,下面是一個(gè)單文件組件

<template>
    <div id="app">
        <span class="counter">
            {{counter}}
            <button @click="increase">+1</button>
        </span>

        <div class="msg-panel">
            <span v-if="showMsg">
                hello, world
            </span>
            <button @click="toggleShowCondition">{{showWhenOdd ? '奇數(shù)展示' : '偶數(shù)展示'}}</button>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            counter: 0,
            showWhenOdd: true
        };
    },
    computed: {
        showMsg() {
            return this.counter % 2 === 0
                ? this.showWhenOdd
                : !this.showWhenOdd;
        }
    },
    mounted() {
        this.counter = Math.floor(Math.random() * 9) + 1;
    },
    methods: {
        increase() {
            this.counter++;
        },
        toggleShowCondition() {
            this.showWhenOdd = !this.showWhenOdd;
        }
    }

};

</script>

<style scoped>
#app {
    text-align: center;
    margin-top: 60px;
}

.counter {
    width: 100px;
    height: 100px;
    border: 1px solid;
    padding: 10px;
}

.msg-panel {
    width: 100px;
    height: 100px;
    margin: 50px auto;
    border: 1px solid;
    padding: 10px;
}
</style>

效果如下:

image.png

這個(gè)組件有兩個(gè)功能

  1. 展示一個(gè)計(jì)數(shù)器,計(jì)數(shù)器初始化計(jì)數(shù)是1-10之間的一個(gè)隨機(jī)數(shù),每次點(diǎn)擊"+1"按鈕會(huì)讓計(jì)數(shù)加1,計(jì)數(shù)實(shí)時(shí)展示在界面上
  2. 信息展示,僅當(dāng)計(jì)數(shù)為偶數(shù)時(shí)候,才會(huì)展示"hello, world"信息。也可以通過(guò)點(diǎn)擊"奇數(shù)展示"按鈕來(lái)控制 僅當(dāng)計(jì)數(shù)為奇數(shù)時(shí)候展示信息。

理解了上面的功能的實(shí)現(xiàn)之后我們?cè)賮?lái)看下使用組合式API如何實(shí)現(xiàn)相同的功能。

<template>
    <div id="app">
        <span class="counter">
            {{counter}}
            <button @click="increase">+1</button>
        </span>

        <div class="msg-panel">
            <span v-if="showMsg">
                hello, world
            </span>
            <button @click="toggleShowCondition">{{showWhenOdd ? '奇數(shù)展示' : '偶數(shù)展示'}}</button>
        </div>
    </div>
</template>

<script>
import {ref, onMounted, computed} from 'vue';

export default {
    setup() {
        // counter邏輯
        const counter = ref(0);
        const increase = () => {
            counter.value++;
        };
        onMounted(() => {
            counter.value = Math.floor(Math.random() * 9) + 1;
        });

        // 提示信息邏輯
        const showWhenOdd = ref(true);
        const showMsg = computed(() => {
            return counter.value % 2 === 0
                ? showWhenOdd.value
                : !showWhenOdd.value;
        });
        const toggleShowCondition = () => {
            showWhenOdd.value = !showWhenOdd.value;
        };

        return {
            counter,
            increase,
            showWhenOdd,
            showMsg,
            toggleShowCondition
        };
    }
};

</script>

<style scoped>
#app {
    text-align: center;
    margin-top: 60px;
}

.counter {
    width: 100px;
    height: 100px;
    border: 1px solid;
    padding: 10px;
}

.msg-panel {
    width: 100px;
    height: 100px;
    margin: 50px auto;
    border: 1px solid;
    padding: 10px;
}
</style>

上面使用了組合式API的示例實(shí)現(xiàn)了相同的功能,注意:

  1. 之前的各種Vue組件的屬性 data/methods/computed/mounted等被setup方法代替,setup方法返回的值里面包括模板需要用到的數(shù)據(jù)和方法。
  2. 之前組件的data里面的數(shù)據(jù)(counter、showWhenOdd)用ref(<初始值>)代替,數(shù)據(jù)使用時(shí)候,需要通過(guò).value取值。
  3. 之前寫(xiě)在methods中的方法都在setup方法里面實(shí)現(xiàn)并返回。
  4. 之前寫(xiě)在computed中的計(jì)算屬性通過(guò)vue中的computed實(shí)現(xiàn),computed接受一個(gè)函數(shù),函數(shù)返回計(jì)算結(jié)果。
  5. 之前生命周期鉤子mounted被setup中通過(guò)onMounted包裹的函數(shù)來(lái)代替。

對(duì)比兩段代碼我們可以看出,使用setup API的實(shí)現(xiàn)明顯可以將兩段功能邏輯(counter展示提示信息)各自聚合在一起

image.png

這樣就實(shí)現(xiàn)了我們之前提到的“讓功能模塊代碼(關(guān)注點(diǎn))聚合在一起,使可讀性和可維護(hù)性更高”的效果,如果我們還希望實(shí)現(xiàn)“提供了一種功能模塊級(jí)別的復(fù)用代碼的方式”,我們可以這樣組織代碼:

<template>
    <div id="app">
        <span class="counter">
            {{counter}}
            <button @click="increase">+1</button>
        </span>

        <div class="msg-panel">
            <span v-if="showMsg">
                hello, world
            </span>
            <button @click="toggleShowCondition">{{showWhenOdd ? '奇數(shù)展示' : '偶數(shù)展示'}}</button>
        </div>
    </div>
</template>

<script>
import {ref, onMounted, computed} from 'vue';

const useCounter = () => {
    // counter邏輯
    const counter = ref(0);
    const increase = () => {
        counter.value++;
    };
    onMounted(() => {
        counter.value = Math.floor(Math.random() * 9) + 1;
    });

    return {
        counter,
        increase
    };
};

const useMsg = counter => {
    const showWhenOdd = ref(true);
    const showMsg = computed(() => {
        return counter.value % 2 === 0
            ? showWhenOdd.value
            : !showWhenOdd.value;
    });
    const toggleShowCondition = () => {
        showWhenOdd.value = !showWhenOdd.value;
    };

    return {
        showWhenOdd,
        showMsg,
        toggleShowCondition
    };
};

export default {
    setup() {
        const {counter, increase} = useCounter();
        const {showWhenOdd, showMsg, toggleShowCondition} = useMsg(counter);

        return {
            counter,
            increase,
            showWhenOdd,
            showMsg,
            toggleShowCondition
        };
    }
};

</script>

<style scoped>
#app {
    text-align: center;
    margin-top: 60px;
}

.counter {
    width: 100px;
    height: 100px;
    border: 1px solid;
    padding: 10px;
}

.msg-panel {
    width: 100px;
    height: 100px;
    margin: 50px auto;
    border: 1px solid;
    padding: 10px;
}
</style>

通過(guò)上面示例可以看到,我們將不同的功能模塊封裝在函數(shù)中,并將關(guān)鍵數(shù)據(jù)和方法返回,然后在Vue組件的setup方法中引用并返回,就可以在模板中使用了。

除了上述的幾個(gè)關(guān)鍵API:setup/ref/computed/onMounted,還有toRef用于解構(gòu)props中的屬性,watch用于監(jiān)聽(tīng)數(shù)據(jù),代替Vue組件中的watch屬性。可以在官方文檔查看用法。

?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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