Vue3?<script?setup?lang=“ts“>?的基本使用
本文主要是講解 <script setup> 與 TypeScript 的基本使用。
1.<script setup> 是什么?
<script setup> 是在單文件組件 (SFC) 中使用 composition api 的編譯時(shí)語(yǔ)法糖。
本文寫(xiě)作時(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。
類(lèi)型檢查使用 vue-tsc 命令。
VSCode:前端最好用的 IDE。
Volar:為 Vue3 的 *.vue 單文件組件提供代碼高亮、語(yǔ)法提示等功能支持的 VSCode 插件;Vue2 你可能是使用的 Vetur 插件,需要禁用 Vetur,下載 Volar,并啟用它。
vue-tsc:類(lèi)型檢查和 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)容,也就是說(shuō)它會(huì)在每次組件實(shí)例被創(chuàng)建的時(shí)候執(zhí)行。
在 <script setup> 聲明的頂層綁定(變量、函數(shù)、import引入的內(nèi)容),都會(huì)自動(dòng)暴露給模板,在模板中直接使用。
<script setup>
import { ref } from 'vue'
// 外部引入的方法,不需要通過(guò) methods 選項(xiàng)來(lái)暴露它,模板可以直接使用
import { getToken } from './utils'
// 外部引入的組件,不需要通過(guò) components 選項(xiàng)來(lái)暴露它,模板可以直接使用
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> 塊中是沒(méi)有組件配置項(xiàng)的,也就是說(shuō)是沒(méi)有 props 選項(xiàng),需要使用 defineProps 來(lái)聲明 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 中的屬性,具有很好的類(lèi)型推斷能力
console.log(props.list[0].age)
</script>
<template>
<h1>{{ msg }}</h1>
<div>{{ title }}</div>
</template>從代碼中可以發(fā)現(xiàn) TS 寫(xiě)法里 props 沒(méi)有定義默認(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ù),和以前的寫(xiě)法一樣
list: () => []
})
// 在 ts 中使用 props 中的屬性,具有很好的類(lèi)型推斷能力
console.log(props.list[0].age)
</script>
<template>
<h1>{{ msg }}</h1>
<div>{{ title }}</div>
</template>
一個(gè)需要注意的地方:在頂層聲明一個(gè)和props的屬性同名的變量,會(huì)有些問(wèn)題。
<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> 塊中也是沒(méi)有組件配置項(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> 中聲明的綁定,即不能通過(guò)模板 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,需要定義類(lèi)型
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 來(lái)訪問(wèn) 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 訪問(wèn)屬性 -->
<div>{{ $attrs.title }}</div>
</template>useSlots
在模板中使用 $slots 來(lái)訪問(wèn) 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中訪問(wèn)插槽默認(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 類(lèi)作為 $style 對(duì)象的鍵暴露給組件,可以直接在模板中使用 $style。而對(duì)于如 <style module="content"> 具名 CSS Modules,編譯后生成的 CSS 類(lèi)作為 content 對(duì)象的鍵暴露給組件,即module 屬性值什么,就暴露什么對(duì)象。
<script setup lang="ts">
import { useCssModule } from 'vue'
// 不傳遞參數(shù),獲取<style module>代碼塊編譯后的css類(lèi)對(duì)象
const style = useCssModule()
console.log(style.success) // 獲取到的是success類(lèi)名經(jīng)過(guò) hash 計(jì)算后的類(lèi)名
// 傳遞參數(shù)content,獲取<style module="content">代碼塊編譯后的css類(lèi)對(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>
<!-- 無(wú)值的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> 是沒(méi)有組件配置項(xiàng) name 的,可以再使用一個(gè)普通的 <script> 來(lái)配置 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> 是沒(méi)有組件配置項(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)語(yǔ)法來(lái)使用掛載在一個(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)簽可以通過(guò) 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)
// 自定義指令
// 必須以 小寫(xiě)字母v開(kāi)頭的小駝峰 的格式來(lái)命名本地自定義指令
// 在模板中使用時(shí),需要用中劃線(xiàn)的格式表示,不可直接使用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)入的指令同樣需要滿(mǎn)足命名規(guī)范
import { directive as vClickOutside } from 'v-click-outside'
</script>
<template>
<div v-click-outside />
</template>更多關(guān)于指令,見(jià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類(lèi)型約束
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
type User = {
name: string
age: number
}
// ref
const msg1 = ref('') // 會(huì)默認(rèn)約束成 string 類(lèi)型,因?yàn)閠s類(lèi)型推導(dǎo)
const msg2 = ref<string>('') // 可以通過(guò)范型約束類(lèi)型
const user1 = ref<User>({ name: 'tang', age: 18 }) // 范型約束
const user2 = ref({} as User) // 類(lèi)型斷言
// 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-04
vue使用@include或@mixin報(bào)錯(cuò)的問(wèn)題及解決
這篇文章主要介紹了vue使用@include或@mixin報(bào)錯(cuò)的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02
解決vue數(shù)據(jù)更新但table內(nèi)容不更新的問(wèn)題
這篇文章主要給大家介紹了vue數(shù)據(jù)更新table內(nèi)容不更新解決方法,文中有詳細(xì)的代碼示例供大家作為參考,感興趣的同學(xué)可以參考閱讀一下2023-08-08
Vue實(shí)現(xiàn)自動(dòng)檢測(cè)以及版本的更新
當(dāng)用戶(hù)在當(dāng)前站點(diǎn)停留時(shí)間比較長(zhǎng),中途站點(diǎn)進(jìn)行升級(jí)更新之后,用戶(hù)如果不刷新頁(yè)面就任然停留在舊的頁(yè)面里,如何讓用戶(hù)收到一個(gè)提示,引導(dǎo)用戶(hù)進(jìn)行更新操作呢?下面給大家介紹如何站點(diǎn)更新如何在生產(chǎn)環(huán)境提示用戶(hù)更新,進(jìn)行頁(yè)面刷新操作,核心原理其實(shí)很簡(jiǎn)單2023-03-03
一文詳解Vue中如何實(shí)現(xiàn)頁(yè)面骨架屏
為了提升頁(yè)面加載速度,我們可以使用頁(yè)面骨架屏技術(shù)來(lái)優(yōu)化用戶(hù)感知,下面就跟隨小編一起學(xué)習(xí)一下如何在vue中實(shí)現(xiàn)頁(yè)面骨架屏吧2024-03-03
Vue無(wú)后端配合實(shí)現(xiàn)導(dǎo)出功能的示例代碼
這篇文章主要為大家詳細(xì)介紹了Vue如何在無(wú)后端配合的情況下實(shí)現(xiàn)導(dǎo)出功能,文中的示例代碼簡(jiǎn)潔易懂,有需要的小伙伴可以跟隨小編一起了解一下2024-01-01
在Vue中實(shí)現(xiàn)隨hash改變響應(yīng)菜單高亮
這篇文章主要介紹了在Vue中實(shí)現(xiàn)隨hash改變響應(yīng)菜單高亮的方法,文中還通過(guò)實(shí)例代碼給大家介紹了vue關(guān)于點(diǎn)擊菜單高亮與組件切換的相關(guān)知識(shí),需要的朋友可以參考下2020-03-03

