0


Vue3 <script setup lang=“ts“> 使用指南

本文主要是讲解

  1. <script setup>

  1. TypeScript

的基本使用。

  1. <script setup>

是什么?

  1. <script setup>

是在单文件组件 (SFC) 中使用

  1. composition api

的编译时语法糖。

本文写作时,

  1. vue

使用的 3.2.26 版本。

1.1. 发展历程

我们先看看

  1. vue3 <script setup>

的发展历程:

  • Vue3 在早期版本( 3.0.0-beta.21 之前)中对 composition api 的支持,只能在组件选项 setup 函数中使用。
  1. <template>
  2. <h1>{{ msg }}</h1>
  3. <button type="button" @click="add">count is: {{ count }}</button>
  4. <ComponentA />
  5. <ComponentB />
  6. </template>
  7. <script>
  8. import { defineComponent, ref } from 'vue'
  9. import ComponentA from '@/components/ComponentA'
  10. import ComponentB from '@/components/ComponentB'
  11. export default defineComponent({
  12. name: 'HelloWorld',
  13. components: { ComponentA, ComponentB },
  14. props: {
  15. msg: String,
  16. },
  17. setup(props, ctx) {
  18. const count = ref(0)
  19. function add() {
  20. count.value++
  21. }
  22. // 使用return {} 把变量、方法暴露给模板
  23. return {
  24. count,
  25. add,
  26. }
  27. },
  28. })
  29. </script>
  • 3.0.0-beta.21 版本中增加了 <script setup> 的实验特性。如果你使用了,会提示你 <script setup> 还处在实验特性阶段。
  • 3.2.0 版本中移除 <script setup> 的实验状态,从此,宣告 <script setup> 正式转正使用,成为框架稳定的特性之一。
  1. <script setup lang="ts">
  2. import { ref } from 'vue'
  3. import ComponentA from '@/components/ComponentA'
  4. import ComponentB from '@/components/ComponentB'
  5. defineProps<{ msg: string }>()
  6. const count = ref(0)
  7. function add() {
  8. count.value++
  9. }
  10. </script>x
  11. <template>
  12. <h1>{{ msg }}</h1>
  13. <button type="button" @click="add">count is: {{ count }}</button>
  14. <ComponentA />
  15. <ComponentB />
  16. </template>

1.2. 优势

与组件选项

  1. setup

函数对比,

  1. <script setup>

的优点:

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

当然,

  1. <script setup>

也是有自己的缺点的,比如需要学习额外的

  1. API

那么

  1. <script setup>

怎么使用呢?有哪些使用要点?与TypeScript如何结合?

2. 使用要点

2.1. 工具

  1. Vue3

单文件组件 (SFC) 的

  1. TS IDE

支持请用

  1. <script setup lang="ts"> + VSCode + Volar

类型检查使用

  1. vue-tsc

命令。

  • VSCode:前端最好用的 IDE
  • Volar:为 Vue3*.vue 单文件组件提供代码高亮、语法提示等功能支持的 VSCode 插件;Vue2 你可能是使用的 Vetur 插件,需要禁用 Vetur,下载 Volar,并启用它。
  • vue-tsc:类型检查和 dts 构建命令行工具。

2.2. 基本用法

  1. setup

属性添加到

  1. <script>

代码块上。

  1. <script setup>
  2. import { ref } from 'vue'
  3. defineProps({
  4. msg: String
  5. })
  6. const count = ref(0)
  7. function add() {
  8. count.value++
  9. }
  10. </script>
  11. <template>
  12. <h1>{{ msg }}</h1>
  13. <button type="button" @click="add">count is: {{ count }}</button>
  14. </template>

若需要使用

  1. TypeScript

,则将

  1. lang

属性添加到

  1. <script>

代码块上,并赋值

  1. ts

  1. <script setup lang="ts">
  2. import { ref } from 'vue'
  3. defineProps<{ msg: string }>()
  4. const count = ref(0)
  5. function add() {
  6. count.value++
  7. }
  8. </script>
  9. <template>
  10. <h1>{{ msg }}</h1>
  11. <button type="button" @click="add">count is: {{ count }}</button>
  12. </template>
  1. <script setup>

块中的脚本会被编译成组件选项

  1. setup

函数的内容,也就是说它会在每次组件实例被创建的时候执行。

  1. <script setup>

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

  1. <script setup>
  2. import { ref } from 'vue'
  3. // 外部引入的方法,不需要通过 methods 选项来暴露它,模板可以直接使用
  4. import { getToken } from './utils'
  5. // 外部引入的组件,不需要通过 components 选项来暴露它,模板可以直接使用
  6. import ComponentA from '@/components/ComponentA'
  7. defineProps({
  8. msg: String
  9. })
  10. // 变量声明,模板可以直接使用
  11. const count = ref(0)
  12. // 函数声明,模板可以直接使用
  13. function add() {
  14. count.value++
  15. }
  16. </script>
  17. <template>
  18. <h1>{{ msg }}</h1>
  19. <h1>{{ getToken() }}</h1>
  20. <button type="button" @click="add">count is: {{ count }}</button>
  21. <ComponentA />
  22. </template>

注意:

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

2.3. 编译器宏

编译器宏(compiler macros) 有:

  1. defineProps

  1. defineEmits

  1. withDefaults

  1. defineExpose

等。

编译器宏只能

  1. <script setup>

块中使用,不需要被导入,并且会在处理

  1. <script setup>

块时被一同编译掉。

编译器宏必须

  1. <script setup>

的顶层使用,不可以在

  1. <script setup>

的局部变量中引用。

defineProps

  1. <script setup>

块中是没有组件配置项的,也就是说是没有

  1. props

选项,需要使用

  1. defineProps

来声明

  1. props

相关信息。

  1. defineProps

接收的对象和组件选项

  1. props

的值一样。

  1. <script setup>
  2. const props = defineProps({
  3. msg: String,
  4. title: {
  5. type: String,
  6. default: '我是标题'
  7. },
  8. list: {
  9. type: Array,
  10. default: () => []
  11. }
  12. })
  13. // 在 js 中使用 props 中的属性
  14. console.log(props.msg)
  15. </script>
  16. <template>
  17. <!-- 在模板中直接使用 props 中声明的变量 -->
  18. <h1>{{ msg }}</h1>
  19. <div>{{ title }}</div>
  20. </template>

TS 版本:

  1. <script setup lang="ts">
  2. interface ListItem {
  3. name: string
  4. age: number
  5. }
  6. const props = defineProps<{
  7. msg: string
  8. title: string
  9. list: ListItem[]
  10. }>()
  11. // 在 ts 中使用 props 中的属性,具有很好的类型推断能力
  12. console.log(props.list[0].age)
  13. </script>
  14. <template>
  15. <h1>{{ msg }}</h1>
  16. <div>{{ title }}</div>
  17. </template>

从代码中可以发现

  1. TS

写法里

  1. props

没有定义默认值。

  1. Vue3

为我们提供了

  1. withDefaults

这个编译器宏,给

  1. props

提供默认值。

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

一个需要注意的地方:在顶层声明一个和

  1. props

的属性同名的变量,会有些问题。

  1. <script setup>
  2. const props = defineProps({
  3. title: {
  4. type: String,
  5. default: '我是标题'
  6. }
  7. })
  8. // 在顶层声明一个和props的属性title同名的变量
  9. const title = '123'
  10. </script>
  11. <template>
  12. <!-- props.title 显示的是 props.title 的值,‘我是标题’ -->
  13. <div>{{ props.title }}</div>
  14. <!-- title 显示的是 在顶层声明的 title 的值,‘123’ -->
  15. <div>{{ title }}</div>
  16. </template>

所以,和组件选项一样,不要定义和

  1. props

的属性同名的顶层变量。

defineEmits

一样的,在

  1. <script setup>

块中也是没有组件配置项

  1. emits

的,需要使用

  1. defineEmits

编译器宏声明

  1. emits

相关信息。

  1. // ./components/HelloWorld.vue
  2. <script setup>
  3. defineProps({
  4. msg: String,
  5. })
  6. const emits = defineEmits(['changeMsg'])
  7. const handleChangeMsg = () => {
  8. emits('changeMsg', 'Hello TS')
  9. }
  10. </script>
  11. <template>
  12. <h1>{{ msg }}</h1>
  13. <button @click="handleChangeMsg">handleChangeMsg</button>
  14. </template>

使用组件:

  1. <script setup>
  2. import { ref } from 'vue'
  3. import HelloWorld from './components/HelloWorld.vue'
  4. const msg = ref('Hello Vue3')
  5. const changeMsg = (v) => {
  6. msg.value = v
  7. }
  8. </script>
  9. <template>
  10. <HelloWorld :msg="msg" @changeMsg="changeMsg" />
  11. </template>

TS 版本:

  1. // ./components/HelloWorld.vue
  2. <script setup lang="ts">
  3. defineProps<{
  4. msg: string
  5. }>()
  6. const emits = defineEmits<{
  7. (e: 'changeMsg', value: string): void
  8. }>()
  9. const handleChangeMsg = () => {
  10. emits('changeMsg', 'Hello TS')
  11. }
  12. </script>
  13. <template>
  14. <h1>{{ msg }}</h1>
  15. <button @click="handleChangeMsg">handleChangeMsg</button>
  16. </template>

使用组件:

  1. <script setup lang="ts">
  2. import { ref } from 'vue'
  3. import HelloWorld from './components/HelloWorld.vue'
  4. const msg = ref('Hello Vue3')
  5. const changeMsg = (v: string) => {
  6. msg.value = v
  7. }
  8. </script>
  9. <template>
  10. <HelloWorld :msg="msg" @changeMsg="changeMsg" />
  11. </template>

defineExpose

  1. Vue3

中,默认不会暴露任何在

  1. <script setup>

中声明的绑定,即不能通过模板

  1. ref

获取到组件实例声明的绑定。

  1. Vue3

提供了

  1. defineExpose

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

  1. // ./components/HelloWorld.vue
  2. <script setup>
  3. import { ref } from 'vue'
  4. const msg = ref('Hello Vue3')
  5. const handleChangeMsg = (v) => {
  6. msg.value = v
  7. }
  8. // 对外暴露的属性
  9. defineExpose({
  10. msg,
  11. handleChangeMsg,
  12. })
  13. </script>

使用组件:

  1. <script setup>
  2. import { ref, onMounted } from 'vue'
  3. import HelloWorld from './components/HelloWorld.vue'
  4. const root = ref(null)
  5. onMounted(() => {
  6. console.log(root.value.msg)
  7. })
  8. const handleChangeMsg = () => {
  9. root.value.handleChangeMsg('Hello TS')
  10. }
  11. </script>
  12. <template>
  13. <HelloWorld ref="root" />
  14. <button @click="handleChangeMsg">handleChangeMsg</button>
  15. </template>

TS 版本:

  1. // ./components/HelloWorld.vue
  2. <script setup lang="ts">
  3. import { ref } from 'vue'
  4. const msg = ref('Hello Vue3')
  5. const handleChangeMsg = (v: string) => {
  6. msg.value = v
  7. }
  8. defineExpose({
  9. msg,
  10. handleChangeMsg
  11. })
  12. </script>
  13. <template>
  14. <h1>{{ msg }}</h1>
  15. </template>

使用组件:

  1. <script setup lang="ts">
  2. import { ref, onMounted } from 'vue'
  3. import HelloWorld from './components/HelloWorld.vue'
  4. // 此处暂时使用any,需要定义类型
  5. const root = ref<any>(null)
  6. onMounted(() => {
  7. console.log(root.value.msg)
  8. })
  9. const handleChangeMsg = () => {
  10. root.value.handleChangeMsg('Hello TS')
  11. }
  12. </script>
  13. <template>
  14. <HelloWorld ref="root" />
  15. <button @click="handleChangeMsg">handleChangeMsg</button>
  16. </template>

2.4. 辅助函数

  1. <script setup>

中常用的辅助函数

  1. hooks api

,主要有:

  1. useAttrs

  1. useSlots

  1. useCssModule

,其他的辅助函数还在实验阶段,不做介绍。

useAttrs

在模板中使用

  1. $attrs

来访问

  1. attrs

数据,与

  1. Vue2

相比,

  1. Vue3

  1. $attrs

还包含了

  1. class

  1. style

属性。

  1. <script setup>

中使用

  1. useAttrs

函数获取

  1. attrs

数据。

  1. <script setup>
  2. import HelloWorld from './components/HelloWorld.vue'
  3. </script>
  4. <template>
  5. <HelloWorld class="hello-word" title="我是标题" />
  6. </template>
  1. // ./components/HelloWorld.vue
  2. <script setup>
  3. import { useAttrs } from 'vue'
  4. const attrs = useAttrs()
  5. // js中使用
  6. console.log(attrs.class) // hello-word
  7. console.log(attrs.title) // 我是标题
  8. </script>
  9. <template>
  10. <!-- 在模板中使用 $attrs 访问属性 -->
  11. <div>{{ $attrs.title }}</div>
  12. </template>

useSlots

在模板中使用

  1. $slots

来访问

  1. slots

数据。

  1. <script setup>

中使用

  1. useSlots

函数获取

  1. slots

插槽数据。

  1. <script setup>
  2. import HelloWorld from './components/HelloWorld.vue'
  3. </script>
  4. <template>
  5. <HelloWorld>
  6. <div>默认插槽</div>
  7. <template v-slot:footer>
  8. <div>具名插槽footer</div>
  9. </template>
  10. </HelloWorld>
  11. </template>
  1. <script setup>
  2. import { useSlots } from 'vue'
  3. const slots = useSlots()
  4. // 在js中访问插槽默认插槽default、具名插槽footer
  5. console.log(slots.default)
  6. console.log(slots.footer)
  7. </script>
  8. <template>
  9. <div>
  10. <!-- 在模板中使用插槽 -->
  11. <slot></slot>
  12. <slot name="footer"></slot>
  13. </div>
  14. </template>

useCssModule

  1. Vue3

中,也是支持

  1. CSS Modules

的,在

  1. <style>

上增加

  1. module

属性,即

  1. <style module>

  1. <style module>

代码块会被编译为

  1. CSS Modules

并且将生成的 CSS 类作为

  1. $style

对象的键暴露给组件,可以直接在模板中使用

  1. $style

。而对于如

  1. <style module="content">

具名

  1. CSS Modules

,编译后生成的 CSS 类作为

  1. content

对象的键暴露给组件,即

  1. module

属性值什么,就暴露什么对象。

  1. <script setup lang="ts">
  2. import { useCssModule } from 'vue'
  3. // 不传递参数,获取<style module>代码块编译后的css类对象
  4. const style = useCssModule()
  5. console.log(style.success) // 获取到的是success类名经过 hash 计算后的类名
  6. // 传递参数content,获取<style module="content">代码块编译后的css类对象
  7. const contentStyle = useCssModule('content')
  8. </script>
  9. <template>
  10. <div class="success">普通style red</div>
  11. <div :class="$style.success">默认CssModule pink</div>
  12. <div :class="style.success">默认CssModule pink</div>
  13. <div :class="contentStyle.success">具名CssModule blue</div>
  14. <div :class="content.success">具名CssModule blue</div>
  15. </template>
  16. <!-- 普通style -->
  17. <style>
  18. .success {
  19. color: red;
  20. }
  21. </style>
  22. <!-- 无值的css module -->
  23. <style module lang="less">
  24. .success {
  25. color: pink;
  26. }
  27. </style>
  28. <!-- 具名的css module -->
  29. <style module="content" lang="less">
  30. .success {
  31. color: blue;
  32. }
  33. </style>

注意,同名的CSS Module,后面的会覆盖前面的。

2.5. 使用组件

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

  1. components

选项中注册。

而在

  1. <script setup>

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

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

  1. <script setup>
  2. import HelloWorld from './HelloWorld.vue'
  3. </script>
  4. <template>
  5. <HelloWorld />
  6. </template>

2.6. 组件name

  1. <script setup>

是没有组件配置项

  1. name

的,可以再使用一个普通的

  1. <script>

来配置

  1. name

  1. // ./components/HelloWorld.vue
  2. <script>
  3. export default {
  4. name: 'HelloWorld'
  5. }
  6. </script>
  7. <script setup>
  8. import { ref } from 'vue'
  9. const total = ref(10)
  10. </script>
  11. <template>
  12. <div>{{ total }}</div>
  13. </template>

使用:

  1. <script setup>
  2. import HelloWorld from './components/HelloWorld.vue'
  3. console.log(HelloWorld.name) // 'HelloWorld'
  4. </script>
  5. <template>
  6. <HelloWorld />
  7. </template>

注意: 如果你设置了

  1. lang

属性,

  1. <script setup>

  1. <script>

  1. lang

需要保持一致。

2.7. inheritAttrs

  1. inheritAttrs

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

  1. true

  1. <script setup>

是没有组件配置项 inheritAttrs 的,可以再使用一个普通的

  1. <script>

  1. <script setup>
  2. import HelloWorld from './components/HelloWorld.vue'
  3. </script>
  4. <template>
  5. <HelloWorld title="我是title"/>
  6. </template>

./components/HelloWorld.vue

  1. <script>
  2. export default {
  3. name: 'HelloWorld',
  4. inheritAttrs: false,
  5. }
  6. </script>
  7. <script setup>
  8. import { useAttrs } from 'vue'
  9. const attrs = useAttrs()
  10. </script>
  11. <template>
  12. <div>
  13. <span :title="attrs.title">hover一下看title</span>
  14. <span :title="$attrs.title">hover一下看title</span>
  15. </div>
  16. </template>

2.8. 顶层await支持

  1. <script setup>

中可以使用顶层 await。结果代码会被编译成

  1. async setup()
  1. <script setup>
  2. const userInfo = await fetch(`/api/post/getUserInfo`)
  3. </script>

注意:

  1. async setup()

必须与

  1. Suspense

组合使用,

  1. Suspense

目前还是处于实验阶段的特性,其 API 可能随时会发生变动,建议暂时不要使用。

2.9. 命名空间组件

  1. vue3

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

  1. // components/Form/index.js
  2. import Form from './Form.vue'
  3. import Input from './Input.vue'
  4. import Label from './Label.vue'
  5. // 把Input、Label组件挂载到 Form 组件上
  6. Form.Input = Input
  7. Form.Label = Label
  8. export default Form
  9. // 使用:
  10. <script setup lang="ts">
  11. import Form from './components/Form'
  12. </script>
  13. <template>
  14. <Form>
  15. <Form.Label />
  16. <Form.Input />
  17. </Form>
  18. </template>
  1. /

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

  1. // FormComponents/index.js
  2. import Input from './Input.vue'
  3. import Label from './Label.vue'
  4. export default {
  5. Input,
  6. Label,
  7. }
  8. // 使用
  9. <script setup>
  10. import * as Form from './FormComponents'
  11. </script>
  12. <template>
  13. <Form.Input>
  14. <Form.Label>label</Form.Label>
  15. </Form.Input>
  16. </template>

2.10. 状态驱动的动态 CSS

  1. Vue3

  1. <style>

标签可以通过

  1. v-bind

这一 CSS 函数将 CSS 的值关联到动态的组件状态上。

  1. <script setup>
  2. const theme = {
  3. color: 'red'
  4. }
  5. </script>
  6. <template>
  7. <p>hello</p>
  8. </template>
  9. <style scoped>
  10. p {
  11. // 使用顶层绑定
  12. color: v-bind('theme.color');
  13. }
  14. </style>

2.11. 指令

全局指令:

  1. <template>
  1. <div v-click-outside />
  1. </template>

自定义指令:

  1. <script setup>
  2. import { ref } from 'vue'
  3. const total = ref(10)
  4. // 自定义指令
  5. // 必须以 小写字母v开头的小驼峰 的格式来命名本地自定义指令
  6. // 在模板中使用时,需要用中划线的格式表示,不可直接使用vMyDirective
  7. const vMyDirective = {
  8. beforeMount: (el, binding, vnode) => {
  9. el.style.borderColor = 'red'
  10. },
  11. updated(el, binding, vnode) {
  12. if (el.value % 2 !== 0) {
  13. el.style.borderColor = 'blue'
  14. } else {
  15. el.style.borderColor = 'red'
  16. }
  17. },
  18. }
  19. const add = () => {
  20. total.value++
  21. }
  22. </script>
  23. <template>
  24. <input :value="total" v-my-directive />
  25. <button @click="add">add+1</button>
  26. </template>

导入的指令:

  1. <script setup>
  2. // 导入的指令同样需要满足命名规范
  3. import { directive as vClickOutside } from 'v-click-outside'
  4. </script>
  5. <template>
  6. <div v-click-outside />
  7. </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类型约束

  1. <script setup lang="ts">
  2. import { ref, reactive, computed } from 'vue'
  3. type User = {
  4. name: string
  5. age: number
  6. }
  7. // ref
  8. const msg1 = ref('') // 会默认约束成 string 类型,因为ts类型推导
  9. const msg2 = ref<string>('') // 可以通过范型约束类型
  10. const user1 = ref<User>({ name: 'tang', age: 18 }) // 范型约束
  11. const user2 = ref({} as User) // 类型断言
  12. // reactive
  13. const obj = reactive({})
  14. const user3 = reactive<User>({ name: 'tang', age: 18 })
  15. const user4 = reactive({} as User)
  16. // computed
  17. const msg3 = computed(() => msg1.value)
  18. const user5 = computed<User>(() => {
  19. return { name: 'tang', age: 18 }
  20. })
  21. </script>

本文转载自: https://blog.csdn.net/qq_27318177/article/details/126399028
版权归原作者 汤玉鹏 所有, 如有侵权,请联系我们删除。

“Vue3 <script setup lang=“ts“> 使用指南”的评论:

还没有评论