Vue3 <script setup lang=“ts“> 的基本使用

 更新时间:2022年12月08日 08:45:25   作者:汤玉鹏  
<script setup> 是在单文件组件 (SFC) 中使用 composition api 的编译时语法糖,本文主要讲解<script setup> 与 TypeScript 的基本使用,感兴趣的朋友跟随小编一起看看吧

本文主要是讲解 <script setup> 与 TypeScript 的基本使用。

1.<script setup> 是什么?

<script setup> 是在单文件组件 (SFC) 中使用 composition api 的编译时语法糖。

本文写作时,vue 使用的 3.2.26 版本。

1.1. 发展历程

我们先看看 vue3 <script setup> 的发展历程:

Vue3 在早期版本( 3.0.0-beta.21 之前)中对 composition api 的支持,只能在组件选项 setup 函数中使用。

<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> 的实验特性。如果你使用了,会提示你 <script setup> 还处在实验特性阶段。
  • 在 3.2.0 版本中移除 <script setup> 的实验状态,从此,宣告 <script setup> 正式转正使用,成为框架稳定的特性之一。
<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. 优势

与组件选项 setup 函数对比, <script setup> 的优点:

  • 更少、更简洁的代码,不需要使用 return {} 暴露变量和方法了,使用组件时不需要主动注册了;
  • 更好的 Typescript 支持,使用纯 Typescript 声明 props 和抛出事件,不会再像 option api 里那么蹩脚了;
  • 更好的运行时性能;

当然, <script setup> 也是有自己的缺点的,比如需要学习额外的 API

那么 <script setup> 怎么使用呢?有哪些使用要点?与TypeScript如何结合?

2. 使用要点

2.1. 工具

Vue3 单文件组件 (SFC) 的 TS IDE 支持请用 <script setup lang="ts"> + VSCode + Volar

类型检查使用 vue-tsc 命令。

VSCode:前端最好用的 IDE

Volar:为 Vue3 的 *.vue 单文件组件提供代码高亮、语法提示等功能支持的 VSCode 插件;Vue2 你可能是使用的 Vetur 插件,需要禁用 Vetur,下载 Volar,并启用它。

vue-tsc:类型检查和 dts 构建命令行工具。

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> 块中的脚本会被编译成组件选项 setup 函数的内容,也就是说它会在每次组件实例被创建的时候执行。

在 <script setup> 声明的顶层绑定(变量、函数、import引入的内容),都会自动暴露给模板,在模板中直接使用。

<script setup>
 
import { ref } from 'vue'
 
// 外部引入的方法,不需要通过 methods 选项来暴露它,模板可以直接使用
 
import { getToken } from './utils'
 
// 外部引入的组件,不需要通过 components 选项来暴露它,模板可以直接使用
 
import ComponentA from '@/components/ComponentA'
 
 
 
defineProps({
 
msg: String
 
})
 
// 变量声明,模板可以直接使用
 
const count = ref(0)
 
// 函数声明,模板可以直接使用
 
function add() {
 
count.value++
 
}
 
</script>
<template>
 
<h1>{{ msg }}</h1>
 
<h1>{{ getToken() }}</h1>
 
<button type="button" @click="add">count is: {{ count }}</button>
 
<ComponentA />
 
</template>

注意:

  • 每个 *.vue 文件最多可同时包含一个 <script> 块 (不包括<script setup>);
  • 每个 *.vue 文件最多可同时包含一个 <script setup> 块 (不包括常规的 <script>);

2.3. 编译器宏

编译器宏(compiler macros) 有:definePropsdefineEmitswithDefaultsdefineExpose 等。

编译器宏只能在 <script setup> 块中使用,不需要被导入,并且会在处理 <script setup> 块时被一同编译掉。

编译器宏必须在 <script setup> 的顶层使用,不可以在 <script setup> 的局部变量中引用。

defineProps

在 <script setup> 块中是没有组件配置项的,也就是说是没有 props 选项,需要使用 defineProps 来声明 props 相关信息。defineProps 接收的对象和组件选项 props 的值一样。

<script setup>
 
const props = defineProps({
 
msg: String,
 
title: {
 
type: String,
 
default: '我是标题'
 
},
 
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>

从代码中可以发现 TS 写法里 props 没有定义默认值。

Vue3 为我们提供了 withDefaults 这个编译器宏,给 props 提供默认值。

<script setup lang="ts">
 
interface ListItem {
 
name: string
 
age: number
 
}
 
interface Props {
 
msg: string
 
// title可选
 
title?: string
 
list: ListItem[]
 
}
 
// withDefaults 的第二个参数便是默认参数设置,会被编译为运行时 props 的 default 选项
 
const props = withDefaults(defineProps<Props>(), {
 
title: '我是标题',
 
// 对于array、object需要使用函数,和以前的写法一样
 
list: () => []
 
})
 
// 在 ts 中使用 props 中的属性,具有很好的类型推断能力
 
console.log(props.list[0].age)
 
</script>
<template>
 
<h1>{{ msg }}</h1>
 
<div>{{ title }}</div>
 
</template>

一个需要注意的地方:在顶层声明一个和props的属性同名的变量,会有些问题。

<script setup>
 
const props = defineProps({
 
title: {
 
type: String,
 
default: '我是标题'
 
}
 
})
 
// 在顶层声明一个和props的属性title同名的变量
 
const title = '123'
</script>
<template>
 
<!-- props.title 显示的是 props.title 的值,‘我是标题' -->
 
<div>{{ props.title }}</div>
 
<!-- title 显示的是 在顶层声明的 title 的值,‘123' -->
 
<div>{{ title }}</div>
 
</template>

所以,和组件选项一样,不要定义和 props 的属性同名的顶层变量。

defineEmits

一样的,在 <script setup> 块中也是没有组件配置项 emits 的,需要使用 defineEmits 编译器宏声明 emits 相关信息。

// ./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中,默认不会暴露任何在 <script setup> 中声明的绑定,即不能通过模板 ref 获取到组件实例声明的绑定。

Vue3 提供了 defineExpose 编译器宏,可以显式地暴露需要暴露的组件中声明的变量和方法。

// ./components/HelloWorld.vue
 
<script setup>
 
import { ref } from 'vue'
 
const msg = ref('Hello Vue3')
const handleChangeMsg = (v) => {
 
msg.value = v
 
}
 
// 对外暴露的属性
 
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'
 
// 此处暂时使用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. 辅助函数

在 <script setup> 中常用的辅助函数hooks api,主要有:useAttrsuseSlotsuseCssModule,其他的辅助函数还在实验阶段,不做介绍。

useAttrs

在模板中使用 $attrs 来访问 attrs 数据,与 Vue2 相比,Vue3 的 $attrs 还包含了 class 和 style 属性。

在 <script setup> 中使用 useAttrs 函数获取 attrs 数据。

<script setup>
 
import HelloWorld from './components/HelloWorld.vue'
 
</script>
<template>
<HelloWorld class="hello-word" title="我是标题" />
 
</template>
// ./components/HelloWorld.vue
 
<script setup>
 
import { useAttrs } from 'vue'
const attrs = useAttrs()
 
// js中使用
 
console.log(attrs.class) // hello-word
 
console.log(attrs.title) // 我是标题
 
</script>
<template>
 
<!-- 在模板中使用 $attrs 访问属性 -->
 
<div>{{ $attrs.title }}</div>
 
</template>

useSlots

在模板中使用 $slots 来访问 slots 数据。

在 <script setup> 中使用 useSlots 函数获取 slots 插槽数据。

<script setup>
 
import HelloWorld from './components/HelloWorld.vue'
 
</script>
<template>
 
<HelloWorld>
 
<div>默认插槽</div>
 
<template v-slot:footer>
 
<div>具名插槽footer</div>
 
</template>
 
</HelloWorld>
 
</template>
<script setup>
 
import { useSlots } from 'vue'
 
 
 
const slots = useSlots()
 
// 在js中访问插槽默认插槽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> 代码块会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style 对象的键暴露给组件,可以直接在模板中使用 $style。而对于如 <style module="content"> 具名 CSS Modules,编译后生成的 CSS 类作为 content 对象的键暴露给组件,即module 属性值什么,就暴露什么对象。

<script setup lang="ts">
 
import { useCssModule } from 'vue'
 
 
 
// 不传递参数,获取<style module>代码块编译后的css类对象
 
const style = useCssModule()
 
console.log(style.success) // 获取到的是success类名经过 hash 计算后的类名
 
 
 
// 传递参数content,获取<style module="content">代码块编译后的css类对象
 
const contentStyle = useCssModule('content')
 
</script>
 
 
 
<template>
 
<div class="success">普通style red</div>
 
 
 
<div :class="$style.success">默认CssModule pink</div>
 
<div :class="style.success">默认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,后面的会覆盖前面的。

2.5. 使用组件

在组件选项中,模板需要使用组件(除了全局组件),需要在 components 选项中注册。

而在 <script setup> 中组件不需要再注册,模板可以直接使用,其实就是相当于一个顶层变量。

建议使用大驼峰(PascalCase)命名组件和使用组件。

<script setup>
 
import HelloWorld from './HelloWorld.vue'
 
</script>
<template>
 
<HelloWorld />
 
</template>

2.6. 组件name

<script setup> 是没有组件配置项 name 的,可以再使用一个普通的 <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>

注意: 如果你设置了 lang 属性,<script setup> 和 <script> 的 lang 需要保持一致。

2.7. inheritAttrs

inheritAttrs 表示是否禁用属性继承,默认值是 true

<script setup> 是没有组件配置项 inheritAttrs 的,可以再使用一个普通的 <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。结果代码会被编译成 async setup()

<script setup>
 
const userInfo = await fetch(`/api/post/getUserInfo`)
 
</script>

注意:async setup() 必须与 Suspense 组合使用,Suspense 目前还是处于实验阶段的特性,其 API 可能随时会发生变动,建议暂时不要使用。

2.9. 命名空间组件

在 vue3 中,我们可以使用点语法来使用挂载在一个对象上的组件。

// 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>

命名空间组件在另外一种场景中的使用,从单个文件中导入多个组件时:

// 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. 状态驱动的动态 CSS

Vue3 中 <style> 标签可以通过 v-bind 这一 CSS 函数将 CSS 的值关联到动态的组件状态上。

<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开头的小驼峰 的格式来命名本地自定义指令
 
// 在模板中使用时,需要用中划线的格式表示,不可直接使用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>

导入的指令:

<script setup>
 
// 导入的指令同样需要满足命名规范
 
import { directive as vClickOutside } from 'v-click-outside'
 
</script>
<template>
 
<div v-click-outside />
 
</template>

更多关于指令,见官方文档(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('')  //  会默认约束成 string 类型,因为ts类型推导
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>

到此这篇关于Vue3 <script setup lang=“ts“> 使用指南的文章就介绍到这了,更多相关Vue3 <script setup lang=“ts“>内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vuex-persist 使用场景分析

    vuex-persist 使用场景分析

    Vuex-Persist 是一个用于在 Vuex 中实现持久化状态的库,它可以用来解决应用程序在刷新浏览器或关闭页面后丢失 Vuex store 中状态的问题,本文给大家介绍vuex-persist 可以用来干什么,感兴趣的朋友一起看看吧
    2023-11-11
  • 在vue-cli脚手架中配置一个vue-router前端路由

    在vue-cli脚手架中配置一个vue-router前端路由

    这篇文章主要给大家介绍了在vue-cli脚手架中配置一个vue-router前端路由的相关资料,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面跟着小编一起来学习学习吧。
    2017-07-07
  • 浅谈vue中数据双向绑定的实现原理

    浅谈vue中数据双向绑定的实现原理

    本篇文章主要介绍了浅谈vue中数据双向绑定的实现原理 ,主要使用v-model这个数据双向绑定,有兴趣的可以了解一下
    2017-09-09
  • vue 解决无法对未定义的值,空值或基元值设置反应属性报错问题

    vue 解决无法对未定义的值,空值或基元值设置反应属性报错问题

    这篇文章主要介绍了vue 解决无法对未定义的值,空值或基元值设置反应属性报错问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • vue如何实现Toast轻提示

    vue如何实现Toast轻提示

    这篇文章主要介绍了vue如何实现Toast轻提示,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue项目做屏幕自适应处理方式

    vue项目做屏幕自适应处理方式

    这篇文章主要介绍了vue项目做屏幕自适应处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • 解决vue项目本地启动时无法携带cookie的问题

    解决vue项目本地启动时无法携带cookie的问题

    这篇文章主要介绍了解决vue项目本地启动时无法携带cookie,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • VUE中对object.object和object[object]的使用解读

    VUE中对object.object和object[object]的使用解读

    这篇文章主要介绍了VUE中对object.object和object[object]的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • vue的webcamjs集成方式

    vue的webcamjs集成方式

    这篇文章主要介绍了vue的webcamjs集成方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • Vue精美简洁登录页完整代码实例

    Vue精美简洁登录页完整代码实例

    这篇文章主要给大家介绍了关于Vue精美简洁登录页完整代码的相关资料,通过文中的方法大家可以使用实现简单的用户登录界面,下面通过示例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07

最新评论