Composition API

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

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

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

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

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個生命週期鉤子函式,在官方文件中你可以找到這一則說明:

這意味著,在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>