vue3中的setup()函數(shù)基本使用詳解
在 Vue3 中,setup 函數(shù)是一個(gè)新引入的概念,它代替了之前版本中的 data、computed、methods 等選項(xiàng),用于設(shè)置組件的初始狀態(tài)和邏輯。setup 函數(shù)的引入使得組件的邏輯更加清晰和靈活,本文將主要介紹Setup的基本用法和少量原理
- 更靈活的組織邏輯:setup 函數(shù)可以將相關(guān)邏輯按照功能進(jìn)行組織,使得組件更加清晰和易于維護(hù)。不再受到 Options API 中選項(xiàng)的限制,可以更自由地組織代碼。
- 邏輯復(fù)用:可以將邏輯抽取為可復(fù)用的函數(shù),并在 setup 函數(shù)中進(jìn)行調(diào)用,實(shí)現(xiàn)邏輯的復(fù)用,避免了在 Options API 中通過 mixins 或混入對(duì)象實(shí)現(xiàn)邏輯復(fù)用時(shí)可能出現(xiàn)的問題。
- 更好的類型推斷:由于 setup 函數(shù)本身是一個(gè)普通的 JavaScript 函數(shù),可以更好地與 TypeScript 配合,提供更好的類型推斷和代碼提示。
- 更好的響應(yīng)式處理:setup 函數(shù)中可以使用 ref、reactive 等函數(shù)創(chuàng)建響應(yīng)式數(shù)據(jù),可以更方便地處理組件的狀態(tài),實(shí)現(xiàn)數(shù)據(jù)的動(dòng)態(tài)更新。
- 更細(xì)粒度的生命周期鉤子:setup 函數(shù)中可以使用 onMounted、onUpdated、onUnmounted 等函數(shù)注冊(cè)組件的生命周期鉤子,可以更細(xì)粒度地控制組件的生命周期行為。
- 更好的代碼組織:setup 函數(shù)將組件的邏輯集中在一個(gè)地方,使得代碼更易讀、易維護(hù),并且可以更清晰地看到組件的整體邏輯。
setup()函數(shù)
setup() 鉤子是在組件中使用組合式 API 的入口,通常只在以下情況下使用:
- 需要在非單文件組件中使用組合式 API 時(shí)。
- 需要在基于選項(xiàng)式 API 的組件中集成基于組合式 API 的代碼時(shí)。
其他情況下,都應(yīng)優(yōu)先使用 <script setup> 語(yǔ)法。
1.1 基本使用
我們可以使用響應(yīng)式 API 來(lái)聲明響應(yīng)式的狀態(tài),在 setup() 函數(shù)中返回的對(duì)象會(huì)暴露給模板和組件實(shí)例。其它的選項(xiàng)也可以通過組件實(shí)例來(lái)獲取 setup() 暴露的屬性。
<script>
import { ref } from 'vue'
?
export default {
setup() {
const count = ref(0)
?
// 返回值會(huì)暴露給模板和其他的選項(xiàng)式 API 鉤子
return {
count
}
},
?
mounted() {
console.log(this.count) // 0
}
}
</script>
?
<template>
<button @click="count++">{{ count }}</button>
</template>請(qǐng)注意在模板中訪問從 setup 返回的 ref 時(shí),它會(huì)自動(dòng)淺層解包,因此你無(wú)須再在模板中為它寫 .value。當(dāng)通過 this 訪問時(shí)也會(huì)同樣如此解包。
setup()自身并不含對(duì)組件實(shí)例的訪問權(quán),即在setup()中訪問this會(huì)是undefined。你可以在選項(xiàng)式 API 中訪問組合式 API 暴露的值,但反過來(lái)則不行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>組合式API</title>
</head>
<body>
<div id="app">
{{ count }}
<button @click="add">加1</button>
</div>
</body>
<script src="../lib/vue.global.js"></script>
<script>
const { ref, onMounted } = Vue
Vue.createApp({
setup () {
const count = ref(0)
const add = () => {
count.value += 1
}
onMounted(() => {
console.log(1111)
})
return {
count,
add
}
},
data () {
return {
count: 10
}
},
methods: {
add () {
this.count += 10
}
},
mounted () {
console.log('2222')
}
}).mount('#app')
</script>
</html>生命周期先執(zhí)行 組合式API 后執(zhí)行選項(xiàng)式API,其余以組合式API為優(yōu)先
1.2 訪問 Prop
setup 函數(shù)的第一個(gè)參數(shù)是組件的 props。和標(biāo)準(zhǔn)的組件一致,一個(gè) setup 函數(shù)的 props 是響應(yīng)式的,并且會(huì)在傳入新的 props 時(shí)同步更新。
{
props: {
title: String,
count: Number
},
setup(props) {
console.log(props.title)
console.log(props.count)
}
}請(qǐng)注意如果你解構(gòu)了
props對(duì)象,解構(gòu)出的變量將會(huì)丟失響應(yīng)性。因此我們推薦通過props.xxx的形式來(lái)使用其中的 props。
如果你確實(shí)需要解構(gòu) props 對(duì)象,或者需要將某個(gè) prop 傳到一個(gè)外部函數(shù)中并保持響應(yīng)性,那么你可以使用 toRefs() 和 toRef() 這兩個(gè)工具函數(shù):
{
setup(props) {
// 將 `props` 轉(zhuǎn)為一個(gè)其中全是 ref 的對(duì)象,然后解構(gòu)
const { title } = toRefs(props)
// `title` 是一個(gè)追蹤著 `props.title` 的 ref
console.log(title.value)
?
// 或者,將 `props` 的單個(gè)屬性轉(zhuǎn)為一個(gè) ref
const title = toRef(props, 'title')
}
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>組合式API</title>
</head>
<body>
<div id="app">
<button @click="num++">加1</button> {{ num }}
<my-root :num="num"></my-root>
</div>
</body>
<script src="../lib/vue.global.js"></script>
<template id="root">
<div>{{ num }} -- {{ test }}</div>
</template>
<script>
const { ref, onMounted, computed } = Vue
?
const Root = {
props: ['num'],
template: '#root',
// setup (props) { // 千萬(wàn)不要對(duì) props 解構(gòu)
// console.log('111')
// return {
// test: computed(() => props.num) // 繼續(xù)保持響應(yīng)式
// }
// }
setup ({ num }) {
console.log(num)
return {
test: computed(() => num) // 失去了響應(yīng)式 - test的值不會(huì)發(fā)生改變
}
}
}
Vue.createApp({
setup () {
const num = ref(10000)
return { num }
},
components: {
MyRoot: Root
}
}).mount('#app')
</script>
</html>1.3 Setup的上下文
傳入 setup 函數(shù)的第二個(gè)參數(shù)是一個(gè) Setup 上下文對(duì)象。上下文對(duì)象暴露了其他一些在 setup 中可能會(huì)用到的值:
{
setup(props, context) {
// 透?jìng)?Attributes(非響應(yīng)式的對(duì)象,等價(jià)于 $attrs)
console.log(context.attrs)
?
// 插槽(非響應(yīng)式的對(duì)象,等價(jià)于 $slots)
console.log(context.slots)
?
// 觸發(fā)事件(函數(shù),等價(jià)于 $emit)
console.log(context.emit)
?
// 暴露公共屬性(函數(shù))
console.log(context.expose)
}
}該上下文對(duì)象是非響應(yīng)式的,可以安全地解構(gòu):
{
setup(props, { attrs, slots, emit, expose }) {
...
}
}attrs 和 slots 都是有狀態(tài)的對(duì)象,它們總是會(huì)隨著組件自身的更新而更新。這意味著你應(yīng)當(dāng)避免解構(gòu)它們,并始終通過 attrs.x 或 slots.x 的形式使用其中的屬性。此外還需注意,和 props 不同,attrs 和 slots 的屬性都不是響應(yīng)式的。如果你想要基于 attrs 或 slots 的改變來(lái)執(zhí)行副作用,那么你應(yīng)該在 onBeforeUpdate 生命周期鉤子中編寫相關(guān)邏輯。
expose 函數(shù)用于顯式地限制該組件暴露出的屬性,當(dāng)父組件通過模板引用訪問該組件的實(shí)例時(shí),將僅能訪問 expose 函數(shù)暴露出的內(nèi)容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>setup上下文對(duì)象</title>
</head>
<body>
<div id="app">
<my-com ref="comref" class="myBox" style="color: red" id="box" msg="hello msg" @my-event="getData">
<template #header>
header
</template>
<div>content</div>
<template #footer>
footer
</template>
</my-com>
</div>
</body>
<template id="com">
<div>
<h1>子組件</h1>
<button @click="sendData">發(fā)送數(shù)據(jù)</button>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
const { createApp, ref, onMounted } = Vue
const Com = {
template: '#com',
setup (props, context) {
// attrs 獲取透?jìng)鬟^來(lái)的值
// slots 如果使用了插槽
// emit 子組件給父組件傳值
// expose 子組件暴露給父組件可以調(diào)用的屬性和方法 ---- options API ref獲取子組件的實(shí)例
?
console.log(props)
console.log(context.attrs) // ref 不在透?jìng)髦?
console.log(context.slots)
const sendData = () => { // 子組件給父組件傳值
context.emit('my-event', 1000)
}
?
// 自定義的屬性和方法,供給父組件使用
const a = ref(1)
const b = ref(2)
const c = ref(3)
?
const fn = () => {
a.value = 100
}
?
// 暴露出去的是對(duì)象
context.expose({
a, b, fn
})
?
return {
sendData
}
}
}
?
Vue.createApp({
setup () {
const getData = (val) => { // 接收子組件的值
console.log('666', val)
}
?
const comref = ref() // comref 就是模版中ref="comref"
?
onMounted(() => {
console.log('com', comref.value) // {}
console.log('a', comref.value.a) // 1
console.log('b', comref.value.b) // 2
console.log('c', comref.value.c) // undefined 因?yàn)闆]有暴露
comref.value.fn()
console.log('a', comref.value.a) // 100
})
?
return {
getData,
comref
}
},
components: {
MyCom: Com
}
}).mount('#app')
</script>
</html>在父組件通過ref獲取子組件的實(shí)例的屬性和方法的需求中,需要注意:
1.如果子組件是 選項(xiàng)式API組件,基本不需要做任何操作
2.如果子組件是 組合式API組件,需要通過 context.expose 暴露給父組件需要使用的屬性和方法
3.如果父組件使用 選項(xiàng)式API, 可以通過 this.$refs.refName 訪問到子組件想要你看到的屬性和方法
4.如果父組件使用 組合式API,需要在setup中先創(chuàng)建 refName,然后再訪問子組件想要你看到的屬性和方法(const refName = ref() refName.value.X)
1.4 與渲染函數(shù)一起使用
setup 也可以返回一個(gè)渲染函數(shù),此時(shí)在渲染函數(shù)中可以直接使用在同一作用域下聲明的響應(yīng)式狀態(tài):
{
setup() {
const count = ref(0)
return () => h('div', count.value)
}
}返回一個(gè)渲染函數(shù)將會(huì)阻止我們返回其他東西。對(duì)于組件內(nèi)部來(lái)說(shuō),這樣沒有問題,但如果我們想通過模板引用將這個(gè)組件的方法暴露給父組件,那就有問題
我們可以通過調(diào)用 expose() 解決這個(gè)問題:
{
setup(props, { expose }) {
const count = ref(0)
const increment = () => ++count.value
?
expose({
increment
})
?
return () => h('div', count.value)
}
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>渲染函數(shù)</title>
</head>
<body>
<div id="app">
<button @click="add">加1</button>
<my-child ref="child"></my-child>
</div>
</body>
<script src="../lib/vue.global.js"></script>
<script>
const { h, ref } = Vue
const Child = {
// 寫法1:
// template: `<div>child</div>`
// 寫法2:
// render () {
// return [
// h('div', 'child!')
// ]
// }
// 寫法3
setup (props, { expose }) {
const count = ref(10)
?
const increment = () => {
count.value += 1
}
?
expose({
increment
})
?
// 返回一個(gè)函數(shù) 函數(shù)返回 渲染函數(shù)的結(jié)果
return () => h('div', 'child!!' + count.value)
}
}
?
Vue.createApp({
components: {
MyChild: Child
},
setup () {
const child = ref()
?
const add = () => {
child.value.increment()
}
?
return {
child,
add
}
}
}).mount('#app')
</script>
</html>到此這篇關(guān)于vue3中的setup()函數(shù)詳解的文章就介紹到這了,更多相關(guān)vue3 setup()函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+elementui實(shí)現(xiàn)表格多級(jí)表頭效果
這篇文章主要為大家詳細(xì)介紹了vue?+?elementui實(shí)現(xiàn)表格多級(jí)表頭,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
vuex 多模塊時(shí) 模塊內(nèi)部的mutation和action的調(diào)用方式
這篇文章主要介紹了vuex 多模塊時(shí) 模塊內(nèi)部的mutation和action的調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-07-07
vue實(shí)現(xiàn)導(dǎo)出word文檔的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何使用vue實(shí)現(xiàn)導(dǎo)出word文檔(包括圖片),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
vue項(xiàng)目中路徑使用@和~的區(qū)別及說(shuō)明
這篇文章主要介紹了vue項(xiàng)目中路徑使用@和~的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12

