好久沒發文了,一篇Vue3的Composition API使用奉上


Composition API

Composition API是Vue3中推薦的組件代碼書寫方式,相較於傳統的Options API來說,它能讓業務邏輯處理和后期代碼維護變的更加簡單。

首先我們來看Options API的優缺點,在Options API中,一個組件通常由data()、methods、watch、computed來組成,在這些選項里我們可以將數據和功能進行完美的划分。

OptionsAPI

但是這樣會出現一個問題,隨着代碼量越來越大,我們對一個功能的追蹤也變的越來越困難,因為該功能的不同部分總是分割在了不同的選項中,如我們要追蹤關於數據A的所有代碼時,需要從data()到methods再至watch中尋找所有數據A出現的地方,這十分的麻煩:

OptionsAPI缺點

而Composition API從根本上解決了這種問題,它針對一個數據開展一條業務線,當你需要尋找與該數據相關的邏輯代碼時,它總是一目了然的展現在你的面前,如下圖所示,關於數據A的處理都被封裝在了一個函數中,不管是data()、methods亦或是watch的邏輯代碼都書寫在這一個函數中,這對后期維護來講非常的方便:

compositionapi演示

setup

基本使用

在Composition API中有一個setup(),該函數能夠去代替data()、methods、以及watch和computed,你可以將所有該組件能應用到的代碼邏輯都寫在這個里面,它具有2個參數,props以及context。

讓我們通過Composition API書寫一個最簡單的例子,現在在setup()函數中你不光可以書寫屬性,還可以書寫方法:

<body>
    <div id="app">
        <main>
            <span @click="callbackfn">{{message}}</span>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const app = Vue.createApp({
            setup(props, context) {
                const message = "hello composition api"
                function callbackfn(event) {
                    console.log("why do you point me?");
                }
                return {
                    message,
                    callbackfn
                }
            }
        })
        app.mount("#app")
    </script>
</body>

this的支持

Composition API和Options API兩者可以同時使用,它們是不沖突的。

但是需要注意的是,setup()函數取代了Options API中beforeCreate以及created這2個生命周期鈎子函數,在官方文檔中你可以找到這一則說明:

image-20210922161920322

image.png

這意味着,在setup()函數中你不能使用this訪問到data中的數據項,但是可以在data()中通過$options拿到setup()返回的對象:

<body>
    <div id="app">
        <main>
            <div>{{dataMessage}}</div>
            <div>{{setupMessage}}</div>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const app = Vue.createApp({
            setup(props, context) {
                const setupMessage = "hello composition api"
                // console.log(this.dataMessage);  // Cannot read properties of undefined
                return {
                    setupMessage
                }
            },
            data() {
                const dataMessage = "hello options api"
                console.log(this.$options.setup());  // {setupMessage: 'hello composition api'}
                return {
                    dataMessage
                }
            }
        })
        app.mount("#app")
    </script>
</body>

響應式數據

非響應式支持

Options API中data()返回的數據均是響應式的:

<body>
    <div id="app">
        <main>
            <button @click="number++">+</button>
            &nbsp;{{number}}&nbsp;
            <button @click="number--">-</button>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const app = Vue.createApp({
            data() {
                let number = 0
                return {
                    number
                }
            }
        })
        app.mount("#app")
    </script>
</body>

而Composition API中setup()返回的數據並不是響應式的:

<body>
    <div id="app">
        <main>
            <button @click="number++">+</button>
            &nbsp;{{number}}&nbsp;
            <button @click="number--">-</button>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const app = Vue.createApp({
            setup(props, context) {
                let number = 0
                return {
                    number
                }
            }
        })
        app.mount("#app")
    </script>
</body>

ref

ref能夠讓值類型的數據在Composition API中具有響應式特征,使用前你需要引入它:

const { ref } = Vue;
let number = ref(0)

它本質上是將這個數據進行了proxy包裝,格式如下:

proxy({value:0})

當我們需要在setup()函數內部修改該值時,必須使用該代理器的value屬性進行修改,如:

number.value++

但是在模板中需要修改該值則可直接進行修改:

number++

示例如下:

<body>
    <div id="app">
        <main>
            <!-- 在setup()函數內部修改 -->
            <button @click="add">+</button>
            <!-- 在模板中進行修改 -->
            <button @click="number++">+</button>
            &nbsp;{{number}}&nbsp;
            <!-- 在setup()函數內部修改 -->
            <button @click="sub">-</button>
            <!-- 在模板中進行修改 -->
            <button @click="number--">-</button>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const app = Vue.createApp({
            setup(props, context) {
                const { ref } = Vue;
                let number = ref(0)
                const add = event => {
                    number.value++;
                }
                const sub = event => {
                    number.value--;
                }
                return {
                    number,
                    add,
                    sub
                }
            }
        })
        app.mount("#app")
    </script>
</body>

reactive

reactive能夠讓引用類型的數據在Composition API中具有響應式特征,使用前你需要引入它:

const { reactive } = Vue;
let ary = reactive([1, 2, 3, 4, 5])

它本質上是將這個數據進行了proxy包裝,格式如下:

Proxy {0: 1, 1: 2, 2: 3, 3: 4, 4: 5}

如果是Object,則包裝后的格式如下:

Proxy {name: 'Jack', age: 18, gender: 'male'}

我們不管是在模板中,還是在setup()函數中,都可以直接對其進行操作。

示例如下:

<body>
    <div id="app">
        <main>
            <!-- 在setup()函數內部修改 -->
            <button @click="push">+</button>
            <!-- 在模板中進行修改 -->
            <button @click="ary.push(ary[ary.length-1]+1)">+</button>
            &nbsp;{{ary}}&nbsp;
            <!-- 在setup()函數內部修改 -->
            <button @click="pop">-</button>
            <!-- 在模板中進行修改 -->
            <button @click="ary.pop()">-</button>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const app = Vue.createApp({
            setup(props, context) {
                const { reactive } = Vue;
                let ary = reactive([1, 2, 3, 4, 5])

                const push = event => {
                    const lastValue = ary[ary.length - 1]
                    ary.push(lastValue + 1)
                }
                const pop = event => {
                    ary.pop()
                }

                return {
                    ary,
                    push,
                    pop
                }
            }
        })
        app.mount("#app")
    </script>
</body>

toRefs

有時候我們並不需要return一個完整的對象,而是return一個對象中單獨的某個值,這個時候我們必須通過toRefs對對象進行解構賦值,才能夠獲得響應對象:

<body>
    <div id="app">
        <main>
            <ul>
                <li>
                    <span @click="name = name.toUpperCase()">{{name}}</span>
                </li>
                <li>
                    <span @click="age = '*'+age+'*'">{{age}}</span>
                </li>
                <li>
                    <span @click="gender = gender.toUpperCase()">{{gender}}</span>
                </li>
            </ul>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const app = Vue.createApp({
            setup(props, context) {
                const { reactive, toRefs } = Vue;
                let message = reactive({
                    name: "Jack",
                    age: 18,
                    gender: "male"
                })
                const { name, age, gender } = toRefs(message)
                return {
                    name,
                    age,
                    gender
                }
            }
        })
        app.mount("#app")
    </script>
</body>

它相當於使用ref對每個值進行包裹,所以說在setup()函數內部修改這些被解構出來的值時,需要使用proxy的value屬性進行修改:

setup(props, context) {
    const { reactive, ref } = Vue;
    let message = reactive({
        name: "Jack",
        age: 18,
        gender: "male"
    })
    return {
        name: ref(message.name),
        age: ref(message.age),
        gender: ref(message.gender)
    }
}

toRef

在我們對對象進行解構賦值時,有可能出現沒有的值,這個時候我們得到的結果是undefined。

如果后續對該值進行修改,讓其進行變更時也需要進行響應式支持的話,則必須使用toRef進行解構賦值,如下所示:

<body>
    <div id="app">
        <main>
            <ul>
                <li>{{name}}</li>
                <li>{{age}}</li>
                <li>{{gender}}</li>
                <li>{{score}}</li>
            </ul>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const app = Vue.createApp({
            setup(props, context) {
                const { reactive, toRefs, toRef, ref } = Vue;
                let message = reactive({
                    name: "Jack",
                    age: 18,
                    gender: "male"
                })
                let { name, age, gender } = toRefs(message)
                // 現在獲取的對象是undefined,因為message中沒有score屬性
                let score = toRef(message, "score")

                // 3s后將它修改為100,如果沒有使用toRefs對其進行包裹,那么這種變更則不是響應式的
                // 它相當於 let score = ref(message.score) || ref(undefined);
                setTimeout(() => {
                    score.value = 100
                }, 3000)

                return {
                    name,
                    age,
                    gender,
                    score
                }
            }
        })
        app.mount("#app")
    </script>
</body>

本節陳述

這一小結主要針對Composition API對數據非響應式支持做出的介紹,Vue3中提供了很多種解決方案,最常用的就是上面舉例的4種:

  • ref:讓值類型的數據能夠支持響應式操作
  • reactive:讓引用類型的數據能夠支持響應式操作
  • toRefs:讓解構賦值出的對象和源容器對象之間具備響應式操作
  • toRef:針對解構賦值沒有的對象支持響應式操作

除開reactive,其他諸如ref、toRefs以及toRef的數據變更都需要使用proxy.value屬性進行操作。

組件化開發

props參數

我們都知道,在setup()函數中不能使用this,因此在父子通信時父組件傳遞給子組件的信息需要子組件使用setup()的props參數進行接收,以下是使用方法:

<body>
    <div id="app">
        <cpn :child-recv-data="fatherData"></cpn>
    </div>

    <!-- 子組件模板 -->
    <template id="cpn-tpl">
        <div>
            <span>{{childRecvData}}</span>
        </div>
    </template>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";

        const { ref, reactive, toRefs, toRef } = Vue

        const app = Vue.createApp({
            setup(props, context) {
                const fatherData = ref("father data")
                return {
                    fatherData
                }
            }
        })

        app.component("cpn", {
            template: "#cpn-tpl",
            props: {
                childRecvData: { required: true, type: String }
            },
            setup(props, context) {
                // 這里的props等同於上面的props
                const { childRecvData } = props
                return {
                    childRecvData
                }
            }
        })

        app.mount("#app")

    </script>
</body>

context參數

context參數有3個屬性可供調用:

  • attrs:相當於no_props的屬性繼承
  • slots:能夠獲取組件中的插槽
  • emit:能夠進行自定義事件

首先是context.attrs,如同no_props一樣,它能獲取組件使用時所元素身上所綁定的非動態屬性:

<body>
    <div id="app">
        <cpn data-font-size="font-size:16px" data-font-color="color:#fff"></cpn>
    </div>

    <!-- 子組件模板 -->
    <template id="cpn-tpl">
        <div>
            <span>cpn</span>
        </div>
    </template>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";

        const { ref, reactive, toRefs, toRef } = Vue

        const app = Vue.createApp({})

        app.component("cpn", {
            template: "#cpn-tpl",
            setup(props, context) {
                console.log(context.attrs["data-font-size"]);   // font-size:16px
                console.log(context.attrs["data-font-color"]);  // color:#fff
                return {}
            }
        })

        app.mount("#app")

    </script>
</body>

其次是context.slots,它能獲取組件中的槽位信息:

<body>
    <div id="app">
        <cpn>
            <template v-slot:default>
                <span>default slot</span>
            </template>
        </cpn>
    </div>

    <!-- 子組件模板 -->
    <template id="cpn-tpl">
        <div>
            <slot></slot>
        </div>
    </template>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";

        const { ref, reactive, toRefs, toRef, h } = Vue

        const app = Vue.createApp({})

        app.component("cpn", {
            template: "#cpn-tpl",
            setup(props, context) {
                // {__v_isVNode: true, __v_skip: true, type: 'span', props: null, key: null, …}
                console.log(context.slots.default()[0]);
                // 修改槽位中元素樣式
                return () => h("div", { style: "color:#aaa" }, [context.slots.default()])
            }
        })

        app.mount("#app")

    </script>
</body>

最后是context.emit,它能發送自定義事件,可用於子組件向父組件傳遞信息,這個是最常用的屬性:

<body>
    <div id="app">
        <cpn @read-event="callbackfn"></cpn>
    </div>

    <!-- 子組件模板 -->
    <template id="cpn-tpl">
        <div>
            <span>cpn</span>
        </div>
    </template>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";

        const { ref, reactive, toRefs, toRef, h } = Vue

        const app = Vue.createApp({
            setup(props, context) {
                function callbackfn(...params) {
                    console.log(params);
                    // ['child data']
                }
                return {
                    callbackfn
                }
            }
        })

        app.component("cpn", {
            template: "#cpn-tpl",
            setup(props, context) {
                const childData = "child data"
                context.emit("readEvent", childData)
                return {}
            }
        })

        app.mount("#app")
    </script>
</body>

readonly

通過readonly讓對象不可更改,這種不可更改是更深層次的限定,比如數組嵌套對象時,你既不能修改外部數組中的內容,也不能修改內部對象中的內容。

下面是readonly簡單的使用例子,我們可將它用於組件通信當中,對於一些只能看不能修改的數據非常方便:

<body>
    <div id="app">
        {{message}}
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";

        const { ref, reactive, toRefs, toRef, readonly } = Vue

        const app = Vue.createApp({
            setup(props, context) {
                // 只能看,不能改
                const message = readonly(
                    reactive(
                        [
                            { id: 1, name: "Jack", age: 19 },
                            { id: 1, name: "Tom", age: 18 },
                            { id: 1, name: "Mary", age: 21 }
                        ]
                    )
                )
                return {
                    message
                }
            }
        })

        app.mount("#app")

    </script>
</body>

inject與provide

令人激動的消息,相信在之前學習組件通信時,你對props和emit的通信方式心存怨念,認為這樣太麻煩了。

不錯,有許許多多的人和你具有同樣的想法,這不,在Vue3中迎來了更簡單好用的組件通信方式,即inject()和provide()。

使用它們就像是使用消息發布訂閱系統一樣,你只需要在某一個組件上通過provide()發送出一則數據,那么該Vue應用下所有的組件都可以使用inject()來對該數據進行接收。

這意味着兄弟組件、爺孫組件等都可以直接的進行通信了,而不再是將數據一層一層的進行傳遞。

下面是一個簡單的使用案例:

<body>
    <div id="app">
        <cpn></cpn>
    </div>

    <!-- 子組件模板 -->
    <template id="cpn-tpl">
        <div>
            <span>{{message}}</span>
        </div>
    </template>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";

        const { ref, reactive, toRefs, toRef, readonly, inject, provide } = Vue

        const app = Vue.createApp({
            setup(props, context) {
                const message = readonly(
                    reactive(
                        [
                            { id: 1, name: "Jack", age: 19 },
                            { id: 1, name: "Tom", age: 18 },
                            { id: 1, name: "Mary", age: 21 }
                        ]
                    )
                )
                // 發布數據,指定key和value
                provide("message", message)
                return {}
            }
        })

        app.component("cpn", {
            template: "#cpn-tpl",
            setup(props, context) {
                // 訂閱數據,指定key和defaultValue,如果沒有該數據則采用defaultValue
                const message = inject("message", "default value")
                return {
                    message
                }
            }
        })

        app.mount("#app")

    </script>
</body>

計算屬性

computed

Composition API中的computed使用與Options API中computed的使用已經不同了。

你必須先導入它然后再到setup()中進行定義,示例如下,computed()參數可以是一個function:

<body>
    <div id="app">
        <main>
            <div>
                <span>{{number1}}</span>
            </div>
            <div>
                <span>{{number2}}</span>
            </div>
            <div>
                <! -- 修改number1的值,number2會重新進行計算 -->
                <button @click="number1++">number1 + 1</button>
                <br>
                <button @click="number1--">number1 - 1</button>
            </div>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const { ref, reactive, computed } = Vue;
        const app = Vue.createApp({
            setup(props, context) {
                let number1 = ref(100);
                let number2 = computed(() => {
                    return number1.value * 2
                })
                return {
                    number1,
                    number2
                }
            }
        })
        app.mount("#app")
    </script>
</body>

get和set

Composition API中的computed()參數也可以是一個Object,該Object允許定義get和set方法,這意味着你可以對computed attribute進行重新賦值。

示例如下:

<body>
    <div id="app">
        <main>
            <div>
                <button @click="number++">+</button>
                <span>&nbsp;{{number}}&nbsp;</span>
                <button @click="number--">-</button>
            </div>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const { ref, reactive, computed } = Vue;
        const app = Vue.createApp({
            setup(props, context) {
                let _n = ref(100);
                let number = computed({
                    get() {
                        console.log("computed get()");
                        return _n.value;
                    },
                    set(newValue) {
                        console.log(("computed set()"));
                        _n.value = newValue;
                    }
                })
                return {
                    number
                }
            }
        })
        app.mount("#app")
    </script>
</body>

數據偵聽

watch

同computed一樣,如果你想在Composition API中使用watch進行數據監聽,則必須先導入后使用。

以下是關於watch最基本的使用,當點擊<span>元素時,會觸發watch偵聽:

<body>
    <div id="app">
        <main>
            <span @click="count++">count</span>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const { ref, reactive, watch } = Vue;
        const app = Vue.createApp({
            setup(props, context) {
                let count = ref(0);
                // 要監聽的屬性,回調函數(新值,舊值)
                watch(count, (newValue, oldValue) => {
                    console.log(`count ${oldValue} => ${newValue}`);
                })
                return {
                    count
                }
            }
        })
        app.mount("#app")
    </script>
</body>

Compostion API中的watch允許監聽對象的某一個屬性,這非常的便捷,如下所示我們只偵聽ary中最后一位數據項的變化:

<body>
    <div id="app">
        <main>
            <ul>
                <li v-for="v in ary">
                    <input type="text" v-if="ary.indexOf(v) === ary.length-1" :value="v"
                        @change="callbackfn(ary.indexOf(v), $event)">
                    <input type="text" v-else :value="v" @change="callbackfn(ary.indexOf(v), $event)">
                </li>
            </ul>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const { ref, reactive, watch } = Vue;
        const app = Vue.createApp({
            setup(props, context) {
                let ary = reactive(
                    ["A", "B", "C", "D"]
                );

                function callbackfn(index, event) {
                    ary[index] = event.target.value
                }

                // 第一個參數必須是一個函數,返回你要偵聽的對象屬性
                // 第二個參數是回調函數(新值,舊值)
                // 如下所示,只偵聽ary的最后一個元素變更
                watch(() => ary[ary.length - 1], (newValue, oldValue) => {
                    console.log(`ary last element change : ${oldValue} => ${newValue}`);
                })

                return {
                    ary,
                    callbackfn
                }
            }
        })
        app.mount("#app")
    </script>
</body>

Composition API中的watch現在可以更加方便的實現監聽多個屬性的變化了,相對於Options API中的watch這一點十分強大。

下面這個例子中不管是number1發生改變還是number2發生改變,都將觸發watch的回調函數:

<body>
    <div id="app">
        <main>
            <p><span @click="number1++">{{number1}}</span></p>
            <p><span @click="number2++">{{number2}}</span></p>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const { ref, reactive, watch } = Vue;
        const app = Vue.createApp({
            setup(props, context) {
                let number1 = ref(100);
                let number2 = ref(100);
                // watch(數組<監聽對象1,監聽對象2>, 回調函數(數組<監聽對象1新值,監聽對象1舊值>, 數組<監聽對象2新值,監聽對象2舊值>)=>{})
                watch([number1, number2], (
                    (
                        [number1NewValue, number1OldValue],
                        [number2NewValue, number2OldValue]
                    ) => {
                        console.log(`number1 change ${number1NewValue} ${number1OldValue}`)
                        console.log(`number2 change ${number2NewValue} ${number2OldValue}`)
                    }
                ))
                return {
                    number1,
                    number2
                }
            }
        })
        app.mount("#app")
    </script>
</body>

watch中允許傳入第三個參數配置對象,如下示例:

<body>
    <div id="app">
        <main>
            {{message}}
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const { ref, reactive, watch } = Vue;
        const app = Vue.createApp({
            setup(props, context) {
                let message = reactive(
                    [
                        { id: 1, name: "Jack", age: 19 },
                        { id: 1, name: "Tom", age: 18 },
                        { id: 1, name: "Mary", age: 21 }
                    ]
                )
                watch(message, ((newValue, oldValue) => {
                    console.log(`message change ${oldValue} => ${newValue}`);
                }
                ), {
                    // 及早偵聽,默認為false,如果為true,它將會在頁面一打開就觸發callbackfn,而不是在數據發生變更時才觸發callbackfn
                    // 默認的watch為false,即惰性偵聽,只有在在數據發生變更時才觸發callbackfn
                    immediate: true,
                    // 深度偵聽,默認為true, 即當多層對象嵌套時它會偵聽所有對象內部的變化,而不僅僅是一層
                    deep: true
                })
                return {
                    message,
                }
            }
        })
        app.mount("#app")
    </script>
</body>

watchEffect

Composition API中新增了watchEffect偵聽。

它與watch偵聽的區別在於:

  • watchEffect是對當前setup()函數下所有數據的全局偵聽,而watch只能偵聽一個或者多個,需要我們手動進行配置
  • watchEffect的偵聽回調函數是沒有參數的,而watch偵聽的回調函數是具有參數的
  • watchEffect的偵聽是及早偵聽,而watch的偵聽默認是惰性偵聽(可通過配置項配置為及早偵聽)

如下示例,我們使用watchEffect偵聽當前setup()函數中所有數據的變化:

<body>
    <div id="app">
        <main>
            <table>
                <thead>
                    <tr>
                        <th>name</th>
                        <th>age</th>
                        <th>gender</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>
                            <input type="text" v-model="name">
                        </td>
                        <td>
                            <input type="number" v-model="age">
                        </td>
                        <td>
                            <select v-model="gender">
                                <option value="male">male</option>
                                <option value="female">female</option>
                            </select>
                        </td>
                    </tr>
                </tbody>
            </table>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const { ref, reactive, watchEffect } = Vue;
        const app = Vue.createApp({
            setup(props, context) {
                let name = ref("");
                let age = ref(0);
                let gender = ref("male");
                // 會監聽name、age、gender。
                // 只能拿到當前的值,不能拿到之前的值
                watchEffect(() => {
                    console.log("start watchEffect");
                    console.log(name.value);
                    console.log(age.value);
                    console.log(gender.value);
                })
                return {
                    name,
                    age,
                    gender
                }
            }
        })
        app.mount("#app")
    </script>
</body>

其他知識

鈎子函數變更

在Options API中如果你需要定義生命周期鈎子函數,則只需要新增對應的選項即可,如:

"use strict";
const app = Vue.createApp({
    beforeCreate(){
        console.log("beforeCreate");
    },
    created(){
        console.log("created");
    }
})
app.mount("#app")

而在Composition API中,你必須先導入這些鈎子函數,然后在setup()函數中對它們進行使用,注意導入時需要加上前綴on,如下所示:

"use strict";
const { onBeforeMount, onMounted } = Vue;
const app = Vue.createApp({
    setup(props, context) {
        onBeforeMount(() => {
            console.log("beforeMount");
        })
        onMounted(() => {
            console.log("mounted");
        })
    }
})
app.mount("#app")

官方例舉了它們詳細的變更記錄,如下表所示:

Options API Composition API
beforeCreate 沒有了,被setup()函數取代了
created 沒有了,被setup()函數取代了
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered
activated onActivated
deactivated onDeactivated

輔助性函數

setup()中可以定義任何數據或者對象,當你的業務非常復雜時,我們也可以定義多個輔助性函數來讓代碼結構更清晰,如下所示:

<body>
    <div id="app">
        <button @click="aData.callbackfn">{{aData.a}}</button>
        <button @click="bData.callbackfn">{{bData.b}}</button>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";


        const { ref, reactive, computed } = Vue;

        // 數據A相關邏輯
        function logicA() {
            const _a = ref("a");
            const a = computed({
                get() {
                    return _a.value
                }
            })
            const callbackfn = () => {
                console.log("hello a");
            }
            return {
                a,
                callbackfn
            }
        }

        // 數據B相關邏輯
        function logicB() {
            const _b = ref("b");
            const b = computed({
                get() {
                    return _b.value
                }
            })
            const callbackfn = () => {
                console.log("hello b");
            }

            return {
                b,
                callbackfn
            }
        }


        const app = Vue.createApp({
            setup(props, context) {
                // 調用輔助性函數
                const aData = reactive(logicA());
                console.log(aData);
                const bData = reactive(logicB());
                return {
                    aData,
                    bData
                }
            }
        })
        app.mount("#app")
    </script>
</body>

獲取真實DOM對象

在某些時候我們需要獲取真實的一個DOM對象,該如何做呢?

其實你可以為這個元素綁定一個ref屬性,該ref屬性指向setup()函數中的一個變量。

然后我們可以通過這個變量的value屬性拿到真實的DOM對象,整體流程如下所示:

<body>
    <div id="app">
        <main>
            <!-- 1.指定需要綁定的變量 -->
            <span ref="spanNode">span</span>
            <div ref="divNode">div</div>
        </main>
    </div>
    <script src='https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.js'></script>
    <script>
        "use strict";
        const { ref, reactive, onMounted } = Vue;
        const app = Vue.createApp({
            setup(props, context) {
                // 2. 綁定的變量必須通過ref進行包裹
                let spanNode = ref(null);
                let divNode = ref(null);
                // 3.接下來你就可以通過value屬性拿到DOM元素
                onMounted(() => {
                    {
                        console.log(spanNode.value);  // <span>span</span>
                        console.log(divNode.value);  // <div>div</div>
                    }
                })
                // 你必須將它們返回出去
                return {
                    spanNode,
                    divNode
                }
            }
        })
        app.mount("#app")
    </script>
</body>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM