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>
{{number}}
<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>
{{number}}
<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>
{{number}}
<!-- 在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>
{{ary}}
<!-- 在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> {{number}} </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>