app开发定制公司Vue3 <script setup lang=“ts“> 使用指南

app开发定制公司本文主要是讲解 <script setup> 与 TypeScript app开发定制公司的基本使用。

<script setup> 是什么?

<script setup> app开发定制公司是在单文件组件 () 中使用 composition api 的编译时。

app开发定制公司本文写作时,vue 使用的 3.2.26 版本。

1.1. 发展历程

app开发定制公司我们先看看 vue3 <script setup> app开发定制公司的发展历程:

  • Vue3 app开发定制公司在早期版本( 3.0.0-beta.21 之前)中对 composition api 的支持,app开发定制公司只能在组件选项 setup app开发定制公司函数中使用。
  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 {} 把变量、app开发定制公司方法暴露给模板
  23. return {
  24. count,
  25. add,
  26. }
  27. },
  28. })
  29. </script>

  • 在 3.0.0-beta.21 app开发定制公司版本中增加了 <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. 优势

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

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

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

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

2. 使用要点

2.1. 工具

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

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

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

2.2. 基本用法

将 setup 属性添加到 <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>

若需要使用 TypeScript,则将 lang 属性添加到 <script> 代码块上,并赋值 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>

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

在 <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) 有:definePropsdefineEmitswithDefaultsdefineExpose 等。

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

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

defineProps

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

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

Vue3 为我们提供了 withDefaults 这个编译器宏,给 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>

一个需要注意的地方:在顶层声明一个和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>

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

defineEmits

一样的,在 <script setup> 块中也是没有组件配置项 emits 的,需要使用 defineEmits 编译器宏声明 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

在 Vue3中,默认不会暴露任何在 <script setup> 中声明的绑定,即不能通过模板 ref 获取到组件实例声明的绑定。

Vue3 提供了 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. 辅助函数

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

useAttrs

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

在 <script setup> 中使用 useAttrs 函数获取 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

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

在 <script setup> 中使用 useSlots 函数获取 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

在 Vue3 中,也是支持 CSS Modules 的,在 <style> 上增加 module 属性,即<style module>

<style module> 代码块会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style 对象的键暴露给组件,可以直接在模板中使用 $style。而对于如 <style module="content"> 具名 CSS Modules,编译后生成的 CSS 类作为 content 对象的键暴露给组件,即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. 使用组件

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

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

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

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

2.6. 组件name

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

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

2.7. inheritAttrs

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

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

<script setup> 中可以使用顶层 await。结果代码会被编译成 async setup()

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

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

2.9. 命名空间组件

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

Vue3 中 <style> 标签可以通过 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. 指令

全局指令:

 
<template>
<div v-click-outside />
</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>

更多关于指令,见官方文档()。

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>

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发