Vue3?<script?setup?lang=“ts“>?的基本使用
本文主要是講解 <script setup>
與 TypeScript
的基本使用。
1.<script setup> 是什么?
<script setup>
是在單文件組件 (SFC) 中使用 composition api
的編譯時(shí)語法糖。
本文寫作時(shí),vue
使用的 3.2.26 版本。
1.1. 發(fā)展歷程
我們先看看 vue3 <script setup>
的發(fā)展歷程:
Vue3
在早期版本( 3.0.0-beta.21
之前)中對(duì) composition api
的支持,只能在組件選項(xiàng) setup
函數(shù)中使用。
<template> <h1>{{ msg }}</h1> <button type="button" @click="add">count is: {{ count }}</button> <ComponentA /> <ComponentB /> </template> <script> import { defineComponent, ref } from 'vue' import ComponentA from '@/components/ComponentA' import ComponentB from '@/components/ComponentB' export default defineComponent({ name: 'HelloWorld', components: { ComponentA, ComponentB }, props: { msg: String, }, setup(props, ctx) { const count = ref(0) function add() { count.value++ } // 使用return {} 把變量、方法暴露給模板 return { count, add, } }, }) </script>
- 在 3.0.0-beta.21 版本中增加了
<script setup>
的實(shí)驗(yàn)特性。如果你使用了,會(huì)提示你<script setup>
還處在實(shí)驗(yàn)特性階段。 - 在 3.2.0 版本中移除
<script setup>
的實(shí)驗(yàn)狀態(tài),從此,宣告<script setup>
正式轉(zhuǎn)正使用,成為框架穩(wěn)定的特性之一。
<script setup lang="ts"> import { ref } from 'vue' import ComponentA from '@/components/ComponentA' import ComponentB from '@/components/ComponentB' defineProps<{ msg: string }>() const count = ref(0) function add() { count.value++ } </script>x <template> <h1>{{ msg }}</h1> <button type="button" @click="add">count is: {{ count }}</button> <ComponentA /> <ComponentB /> </template>
1.2. 優(yōu)勢(shì)
與組件選項(xiàng) setup
函數(shù)對(duì)比, <script setup>
的優(yōu)點(diǎn):
- 更少、更簡(jiǎn)潔的代碼,不需要使用
return {}
暴露變量和方法了,使用組件時(shí)不需要主動(dòng)注冊(cè)了; - 更好的
Typescript
支持,使用純Typescript
聲明props
和拋出事件,不會(huì)再像option api
里那么蹩腳了; - 更好的運(yùn)行時(shí)性能;
當(dāng)然, <script setup>
也是有自己的缺點(diǎn)的,比如需要學(xué)習(xí)額外的 API
。
那么 <script setup>
怎么使用呢?有哪些使用要點(diǎn)?與TypeScript如何結(jié)合?
2. 使用要點(diǎn)
2.1. 工具
Vue3
單文件組件 (SFC) 的 TS IDE
支持請(qǐng)用 <script setup lang="ts"> + VSCode + Volar
。
類型檢查使用 vue-tsc
命令。
VSCode:前端最好用的 IDE
。
Volar:為 Vue3
的 *.vue
單文件組件提供代碼高亮、語法提示等功能支持的 VSCode
插件;Vue2
你可能是使用的 Vetur
插件,需要禁用 Vetur
,下載 Volar
,并啟用它。
vue-tsc:類型檢查和 dts
構(gòu)建命令行工具。
2.2. 基本用法
將 setup
屬性添加到 <script>
代碼塊上。
<script setup> import { ref } from 'vue' defineProps({ msg: String }) const count = ref(0) function add() { count.value++ } </script> <template> <h1>{{ msg }}</h1> <button type="button" @click="add">count is: {{ count }}</button> </template>
若需要使用 TypeScript
,則將 lang
屬性添加到 <script>
代碼塊上,并賦值 ts
。
<script setup lang="ts"> import { ref } from 'vue' defineProps<{ msg: string }>() const count = ref(0) function add() { count.value++ } </script> <template> <h1>{{ msg }}</h1> <button type="button" @click="add">count is: {{ count }}</button> </template>
<script setup>
塊中的腳本會(huì)被編譯成組件選項(xiàng) setup
函數(shù)的內(nèi)容,也就是說它會(huì)在每次組件實(shí)例被創(chuàng)建的時(shí)候執(zhí)行。
在 <script setup>
聲明的頂層綁定(變量、函數(shù)、import引入的內(nèi)容),都會(huì)自動(dòng)暴露給模板,在模板中直接使用。
<script setup> import { ref } from 'vue' // 外部引入的方法,不需要通過 methods 選項(xiàng)來暴露它,模板可以直接使用 import { getToken } from './utils' // 外部引入的組件,不需要通過 components 選項(xiàng)來暴露它,模板可以直接使用 import ComponentA from '@/components/ComponentA' defineProps({ msg: String }) // 變量聲明,模板可以直接使用 const count = ref(0) // 函數(shù)聲明,模板可以直接使用 function add() { count.value++ } </script> <template> <h1>{{ msg }}</h1> <h1>{{ getToken() }}</h1> <button type="button" @click="add">count is: {{ count }}</button> <ComponentA /> </template>
注意:
- 每個(gè)
*.vue
文件最多可同時(shí)包含一個(gè)<script>
塊 (不包括<script setup>
); - 每個(gè)
*.vue
文件最多可同時(shí)包含一個(gè)<script setup>
塊 (不包括常規(guī)的<script>
);
2.3. 編譯器宏
編譯器宏(compiler macros) 有:defineProps
、defineEmits
、withDefaults
、defineExpose
等。
編譯器宏只能在 <script setup>
塊中使用,不需要被導(dǎo)入,并且會(huì)在處理 <script setup>
塊時(shí)被一同編譯掉。
編譯器宏必須在 <script setup>
的頂層使用,不可以在 <script setup>
的局部變量中引用。
defineProps
在 <script setup>
塊中是沒有組件配置項(xiàng)的,也就是說是沒有 props
選項(xiàng),需要使用 defineProps
來聲明 props
相關(guān)信息。defineProps
接收的對(duì)象和組件選項(xiàng) props
的值一樣。
<script setup> const props = defineProps({ msg: String, title: { type: String, default: '我是標(biāo)題' }, list: { type: Array, default: () => [] } }) // 在 js 中使用 props 中的屬性 console.log(props.msg) </script> <template> <!-- 在模板中直接使用 props 中聲明的變量 --> <h1>{{ msg }}</h1> <div>{{ title }}</div> </template>
TS 版本:
<script setup lang="ts"> interface ListItem { name: string age: number } const props = defineProps<{ msg: string title: string list: ListItem[] }>() // 在 ts 中使用 props 中的屬性,具有很好的類型推斷能力 console.log(props.list[0].age) </script> <template> <h1>{{ msg }}</h1> <div>{{ title }}</div> </template>
從代碼中可以發(fā)現(xiàn) TS
寫法里 props
沒有定義默認(rèn)值。
Vue3
為我們提供了 withDefaults
這個(gè)編譯器宏,給 props
提供默認(rèn)值。
<script setup lang="ts"> interface ListItem { name: string age: number } interface Props { msg: string // title可選 title?: string list: ListItem[] } // withDefaults 的第二個(gè)參數(shù)便是默認(rèn)參數(shù)設(shè)置,會(huì)被編譯為運(yùn)行時(shí) props 的 default 選項(xiàng) const props = withDefaults(defineProps<Props>(), { title: '我是標(biāo)題', // 對(duì)于array、object需要使用函數(shù),和以前的寫法一樣 list: () => [] }) // 在 ts 中使用 props 中的屬性,具有很好的類型推斷能力 console.log(props.list[0].age) </script> <template> <h1>{{ msg }}</h1> <div>{{ title }}</div> </template>
一個(gè)需要注意的地方:在頂層聲明一個(gè)和props
的屬性同名的變量,會(huì)有些問題。
<script setup> const props = defineProps({ title: { type: String, default: '我是標(biāo)題' } }) // 在頂層聲明一個(gè)和props的屬性title同名的變量 const title = '123' </script> <template> <!-- props.title 顯示的是 props.title 的值,‘我是標(biāo)題' --> <div>{{ props.title }}</div> <!-- title 顯示的是 在頂層聲明的 title 的值,‘123' --> <div>{{ title }}</div> </template>
所以,和組件選項(xiàng)一樣,不要定義和 props
的屬性同名的頂層變量。
defineEmits
一樣的,在 <script setup>
塊中也是沒有組件配置項(xiàng) emits
的,需要使用 defineEmits
編譯器宏聲明 emits
相關(guān)信息。
// ./components/HelloWorld.vue <script setup> defineProps({ msg: String, }) const emits = defineEmits(['changeMsg']) const handleChangeMsg = () => { emits('changeMsg', 'Hello TS') } </script> <template> <h1>{{ msg }}</h1> <button @click="handleChangeMsg">handleChangeMsg</button> </template>
使用組件:
<script setup> import { ref } from 'vue' import HelloWorld from './components/HelloWorld.vue' const msg = ref('Hello Vue3') const changeMsg = (v) => { msg.value = v } </script> <template> <HelloWorld :msg="msg" @changeMsg="changeMsg" /> </template>
TS 版本:
// ./components/HelloWorld.vue <script setup lang="ts"> defineProps<{ msg: string }>() const emits = defineEmits<{ (e: 'changeMsg', value: string): void }>() const handleChangeMsg = () => { emits('changeMsg', 'Hello TS') } </script> <template> <h1>{{ msg }}</h1> <button @click="handleChangeMsg">handleChangeMsg</button> </template>
使用組件:
<script setup lang="ts"> import { ref } from 'vue' import HelloWorld from './components/HelloWorld.vue' const msg = ref('Hello Vue3') const changeMsg = (v: string) => { msg.value = v } </script> <template> <HelloWorld :msg="msg" @changeMsg="changeMsg" /> </template>
defineExpose
在 Vue3
中,默認(rèn)不會(huì)暴露任何在 <script setup>
中聲明的綁定,即不能通過模板 ref
獲取到組件實(shí)例聲明的綁定。
Vue3
提供了 defineExpose
編譯器宏,可以顯式地暴露需要暴露的組件中聲明的變量和方法。
// ./components/HelloWorld.vue <script setup> import { ref } from 'vue' const msg = ref('Hello Vue3') const handleChangeMsg = (v) => { msg.value = v } // 對(duì)外暴露的屬性 defineExpose({ msg, handleChangeMsg, }) </script>
使用組件:
<script setup> import { ref, onMounted } from 'vue' import HelloWorld from './components/HelloWorld.vue' const root = ref(null) onMounted(() => { console.log(root.value.msg) }) const handleChangeMsg = () => { root.value.handleChangeMsg('Hello TS') } </script> <template> <HelloWorld ref="root" /> <button @click="handleChangeMsg">handleChangeMsg</button> </template>
TS 版本:
// ./components/HelloWorld.vue <script setup lang="ts"> import { ref } from 'vue' const msg = ref('Hello Vue3') const handleChangeMsg = (v: string) => { msg.value = v } defineExpose({ msg, handleChangeMsg }) </script> <template> <h1>{{ msg }}</h1> </template>
使用組件:
<script setup lang="ts"> import { ref, onMounted } from 'vue' import HelloWorld from './components/HelloWorld.vue' // 此處暫時(shí)使用any,需要定義類型 const root = ref<any>(null) onMounted(() => { console.log(root.value.msg) }) const handleChangeMsg = () => { root.value.handleChangeMsg('Hello TS') } </script> <template> <HelloWorld ref="root" /> <button @click="handleChangeMsg">handleChangeMsg</button> </template>
2.4. 輔助函數(shù)
在 <script setup>
中常用的輔助函數(shù)hooks api
,主要有:useAttrs
、useSlots
、useCssModule
,其他的輔助函數(shù)還在實(shí)驗(yàn)階段,不做介紹。
useAttrs
在模板中使用 $attrs
來訪問 attrs
數(shù)據(jù),與 Vue2
相比,Vue3
的 $attrs
還包含了 class
和 style
屬性。
在 <script setup>
中使用 useAttrs
函數(shù)獲取 attrs
數(shù)據(jù)。
<script setup> import HelloWorld from './components/HelloWorld.vue' </script> <template> <HelloWorld class="hello-word" title="我是標(biāo)題" /> </template>
// ./components/HelloWorld.vue <script setup> import { useAttrs } from 'vue' const attrs = useAttrs() // js中使用 console.log(attrs.class) // hello-word console.log(attrs.title) // 我是標(biāo)題 </script> <template> <!-- 在模板中使用 $attrs 訪問屬性 --> <div>{{ $attrs.title }}</div> </template>
useSlots
在模板中使用 $slots
來訪問 slots
數(shù)據(jù)。
在 <script setup>
中使用 useSlots
函數(shù)獲取 slots
插槽數(shù)據(jù)。
<script setup> import HelloWorld from './components/HelloWorld.vue' </script> <template> <HelloWorld> <div>默認(rèn)插槽</div> <template v-slot:footer> <div>具名插槽footer</div> </template> </HelloWorld> </template>
<script setup> import { useSlots } from 'vue' const slots = useSlots() // 在js中訪問插槽默認(rèn)插槽default、具名插槽footer console.log(slots.default) console.log(slots.footer) </script> <template> <div> <!-- 在模板中使用插槽 --> <slot></slot> <slot name="footer"></slot> </div> </template>
useCssModule
在 Vue3
中,也是支持 CSS Modules
的,在 <style>
上增加 module
屬性,即<style module>
。
<style module>
代碼塊會(huì)被編譯為 CSS Modules
并且將生成的 CSS 類作為 $style
對(duì)象的鍵暴露給組件,可以直接在模板中使用 $style
。而對(duì)于如 <style module="content">
具名 CSS Modules
,編譯后生成的 CSS 類作為 content
對(duì)象的鍵暴露給組件,即module
屬性值什么,就暴露什么對(duì)象。
<script setup lang="ts"> import { useCssModule } from 'vue' // 不傳遞參數(shù),獲取<style module>代碼塊編譯后的css類對(duì)象 const style = useCssModule() console.log(style.success) // 獲取到的是success類名經(jīng)過 hash 計(jì)算后的類名 // 傳遞參數(shù)content,獲取<style module="content">代碼塊編譯后的css類對(duì)象 const contentStyle = useCssModule('content') </script> <template> <div class="success">普通style red</div> <div :class="$style.success">默認(rèn)CssModule pink</div> <div :class="style.success">默認(rèn)CssModule pink</div> <div :class="contentStyle.success">具名CssModule blue</div> <div :class="content.success">具名CssModule blue</div> </template> <!-- 普通style --> <style> .success { color: red; } </style> <!-- 無值的css module --> <style module lang="less"> .success { color: pink; } </style> <!-- 具名的css module --> <style module="content" lang="less"> .success { color: blue; } </style>
注意,同名的CSS Module,后面的會(huì)覆蓋前面的。
2.5. 使用組件
在組件選項(xiàng)中,模板需要使用組件(除了全局組件),需要在 components
選項(xiàng)中注冊(cè)。
而在 <script setup>
中組件不需要再注冊(cè),模板可以直接使用,其實(shí)就是相當(dāng)于一個(gè)頂層變量。
建議使用大駝峰(PascalCase)命名組件和使用組件。
<script setup> import HelloWorld from './HelloWorld.vue' </script> <template> <HelloWorld /> </template>
2.6. 組件name
<script setup>
是沒有組件配置項(xiàng) name
的,可以再使用一個(gè)普通的 <script>
來配置 name
。
// ./components/HelloWorld.vue <script> export default { name: 'HelloWorld' } </script> <script setup> import { ref } from 'vue' const total = ref(10) </script> <template> <div>{{ total }}</div> </template>
使用:
<script setup> import HelloWorld from './components/HelloWorld.vue' console.log(HelloWorld.name) // 'HelloWorld' </script> <template> <HelloWorld /> </template>
注意: 如果你設(shè)置了 lang
屬性,<script setup>
和 <script>
的 lang
需要保持一致。
2.7. inheritAttrs
inheritAttrs
表示是否禁用屬性繼承,默認(rèn)值是 true
。
<script setup>
是沒有組件配置項(xiàng) inheritAttrs 的,可以再使用一個(gè)普通的 <script>
。
<script setup> import HelloWorld from './components/HelloWorld.vue' </script> <template> <HelloWorld title="我是title"/> </template>
./components/HelloWorld.vue
<script> export default { name: 'HelloWorld', inheritAttrs: false, } </script> <script setup> import { useAttrs } from 'vue' const attrs = useAttrs() </script> <template> <div> <span :title="attrs.title">hover一下看title</span> <span :title="$attrs.title">hover一下看title</span> </div> </template>
2.8. 頂層await支持
<script setup>
中可以使用頂層 await。結(jié)果代碼會(huì)被編譯成 async setup()
<script setup> const userInfo = await fetch(`/api/post/getUserInfo`) </script>
注意:async setup()
必須與 Suspense
組合使用,Suspense
目前還是處于實(shí)驗(yàn)階段的特性,其 API 可能隨時(shí)會(huì)發(fā)生變動(dòng),建議暫時(shí)不要使用。
2.9. 命名空間組件
在 vue3
中,我們可以使用點(diǎn)語法來使用掛載在一個(gè)對(duì)象上的組件。
// components/Form/index.js import Form from './Form.vue' import Input from './Input.vue' import Label from './Label.vue' // 把Input、Label組件掛載到 Form 組件上 Form.Input = Input Form.Label = Label export default Form // 使用: <script setup lang="ts"> import Form from './components/Form' </script> <template> <Form> <Form.Label /> <Form.Input /> </Form> </template>
命名空間組件在另外一種場(chǎng)景中的使用,從單個(gè)文件中導(dǎo)入多個(gè)組件時(shí):
// FormComponents/index.js import Input from './Input.vue' import Label from './Label.vue' export default { Input, Label, } // 使用 <script setup> import * as Form from './FormComponents' </script> <template> <Form.Input> <Form.Label>label</Form.Label> </Form.Input> </template>
2.10. 狀態(tài)驅(qū)動(dòng)的動(dòng)態(tài) CSS
Vue3
中 <style>
標(biāo)簽可以通過 v-bind
這一 CSS 函數(shù)將 CSS 的值關(guān)聯(lián)到動(dòng)態(tài)的組件狀態(tài)上。
<script setup> const theme = { color: 'red' } </script> <template> <p>hello</p> </template> <style scoped> p { // 使用頂層綁定 color: v-bind('theme.color'); } </style>
2.11. 指令
全局指令:
<template> | |
<div v-click-outside /> | |
</template> |
自定義指令:
<script setup> import { ref } from 'vue' const total = ref(10) // 自定義指令 // 必須以 小寫字母v開頭的小駝峰 的格式來命名本地自定義指令 // 在模板中使用時(shí),需要用中劃線的格式表示,不可直接使用vMyDirective const vMyDirective = { beforeMount: (el, binding, vnode) => { el.style.borderColor = 'red' }, updated(el, binding, vnode) { if (el.value % 2 !== 0) { el.style.borderColor = 'blue' } else { el.style.borderColor = 'red' } }, } const add = () => { total.value++ } </script> <template> <input :value="total" v-my-directive /> <button @click="add">add+1</button> </template>
導(dǎo)入的指令:
<script setup> // 導(dǎo)入的指令同樣需要滿足命名規(guī)范 import { directive as vClickOutside } from 'v-click-outside' </script> <template> <div v-click-outside /> </template>
更多關(guān)于指令,見官方文檔(https://v3.cn.vuejs.org/guide/custom-directive.html#%E7%AE%80%E4%BB%8B、https://v3.cn.vuejs.org/api/application-api.html#directive)。
2.12. Composition Api類型約束
<script setup lang="ts"> import { ref, reactive, computed } from 'vue' type User = { name: string age: number } // ref const msg1 = ref('') // 會(huì)默認(rèn)約束成 string 類型,因?yàn)閠s類型推導(dǎo) const msg2 = ref<string>('') // 可以通過范型約束類型 const user1 = ref<User>({ name: 'tang', age: 18 }) // 范型約束 const user2 = ref({} as User) // 類型斷言 // reactive const obj = reactive({}) const user3 = reactive<User>({ name: 'tang', age: 18 }) const user4 = reactive({} as User) // computed const msg3 = computed(() => msg1.value) const user5 = computed<User>(() => { return { name: 'tang', age: 18 } }) </script>
到此這篇關(guān)于Vue3 <script setup lang=“ts“> 使用指南的文章就介紹到這了,更多相關(guān)Vue3 <script setup lang=“ts“>內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue,angular,avalon這三種MVVM框架優(yōu)缺點(diǎn)
本文給大家具體分析了下vue,angular,avalon這三種MVVM框架優(yōu)缺點(diǎn),十分的細(xì)致全面,有需要的小伙伴可以參考下2016-04-04vue使用@include或@mixin報(bào)錯(cuò)的問題及解決
這篇文章主要介紹了vue使用@include或@mixin報(bào)錯(cuò)的問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02解決vue數(shù)據(jù)更新但table內(nèi)容不更新的問題
這篇文章主要給大家介紹了vue數(shù)據(jù)更新table內(nèi)容不更新解決方法,文中有詳細(xì)的代碼示例供大家作為參考,感興趣的同學(xué)可以參考閱讀一下2023-08-08Vue實(shí)現(xiàn)自動(dòng)檢測(cè)以及版本的更新
當(dāng)用戶在當(dāng)前站點(diǎn)停留時(shí)間比較長(zhǎng),中途站點(diǎn)進(jìn)行升級(jí)更新之后,用戶如果不刷新頁(yè)面就任然停留在舊的頁(yè)面里,如何讓用戶收到一個(gè)提示,引導(dǎo)用戶進(jìn)行更新操作呢?下面給大家介紹如何站點(diǎn)更新如何在生產(chǎn)環(huán)境提示用戶更新,進(jìn)行頁(yè)面刷新操作,核心原理其實(shí)很簡(jiǎn)單2023-03-03一文詳解Vue中如何實(shí)現(xiàn)頁(yè)面骨架屏
為了提升頁(yè)面加載速度,我們可以使用頁(yè)面骨架屏技術(shù)來優(yōu)化用戶感知,下面就跟隨小編一起學(xué)習(xí)一下如何在vue中實(shí)現(xiàn)頁(yè)面骨架屏吧2024-03-03Vue無后端配合實(shí)現(xiàn)導(dǎo)出功能的示例代碼
這篇文章主要為大家詳細(xì)介紹了Vue如何在無后端配合的情況下實(shí)現(xiàn)導(dǎo)出功能,文中的示例代碼簡(jiǎn)潔易懂,有需要的小伙伴可以跟隨小編一起了解一下2024-01-01在Vue中實(shí)現(xiàn)隨hash改變響應(yīng)菜單高亮
這篇文章主要介紹了在Vue中實(shí)現(xiàn)隨hash改變響應(yīng)菜單高亮的方法,文中還通過實(shí)例代碼給大家介紹了vue關(guān)于點(diǎn)擊菜單高亮與組件切換的相關(guān)知識(shí),需要的朋友可以參考下2020-03-03