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> 語法。
1.1 基本使用
我們可以使用響應(yīng)式 API 來聲明響應(yīng)式的狀態(tài),在 setup()
函數(shù)中返回的對(duì)象會(huì)暴露給模板和組件實(shí)例。其它的選項(xiàng)也可以通過組件實(shí)例來獲取 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)淺層解包,因此你無須再在模板中為它寫 .value
。當(dāng)通過 this
訪問時(shí)也會(huì)同樣如此解包。
setup()
自身并不含對(duì)組件實(shí)例的訪問權(quán),即在setup()
中訪問this
會(huì)是undefined
。你可以在選項(xiàng)式 API 中訪問組合式 API 暴露的值,但反過來則不行。
<!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
的形式來使用其中的 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) { // 千萬不要對(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
的改變來執(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)鬟^來的值 // 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)部來說,這樣沒有問題,但如果我們想通過模板引用將這個(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-04vuex 多模塊時(shí) 模塊內(nèi)部的mutation和action的調(diào)用方式
這篇文章主要介紹了vuex 多模塊時(shí) 模塊內(nèi)部的mutation和action的調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-07-07vue實(shí)現(xiàn)導(dǎo)出word文檔的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何使用vue實(shí)現(xiàn)導(dǎo)出word文檔(包括圖片),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01vue項(xiàng)目中路徑使用@和~的區(qū)別及說明
這篇文章主要介紹了vue項(xiàng)目中路徑使用@和~的區(qū)別及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12