阅读 2644

Vue3中 <script setup lang="ts"> 使用总结

Vue3.2发布时,<script setup>语法糖也跟着正式发布了,结合typescript来使用的话,书写Vue组件的体验比之前要舒服太多了。本文总结了一些在<script setup>中常用的书写方式,希望可以为你在使用<script setup>的时候提供帮助。

开发环境搭建

  • 推荐使用vite来快速初始化一个Vue3+Typescript项目,它会配置好一些开箱即用的配置

  • 推荐使用vscode,并且安装volar插件辅助开发

  • 推荐自定义一个代码片段,辅助开发

    // vue.json {   "vue setup ts less": {    "prefix": "vstl",    "body": [    "<template>",    "    <div></div>",    "</template>",    "<script setup lang=\"ts\">",    "    ",    "</script>",    "<style lang=\"less\" scoped>",    "</style>",    ],    "description": "vue3 setup ts less"   } } 复制代码

使用响应式变量

<script setup>中定义的响应式变量默认都是暴露给模板的

  • 使用refref会对传入的初始值做类型推导,如果是复杂的类型,还可以通过泛型参数来指定

<template>     <div>{{ numberRef }}</div> </template> <script setup lang="ts"> import { ref } from 'vue'; const numberRef = ref(0) // ===> Ref<number> const stringRef = ref("") // ===> Ref<string> interface IFoo {     bar: string } const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined> </script> 复制代码

  • 使用reactive:Vue文档介绍了三种方式来为reactive声明类型

<template>     <div>{{ book1.bar }}</div> </template> <script setup lang="ts"> import { reactive } from 'vue'; interface IFoo {     bar: string } // 第一种 const book1 = reactive<IFoo>({ bar: 'bar' }) // 第二种 const book2: IFoo = reactive({ bar: 'bar' }) // 第三种 const book3 = reactive({ bar: 'bar' }) as IFoo </script> 复制代码

使用computed

computed方法会自动推导出类型

  • 基础使用方式

<template>     <div>{{ fooComputed?.bar }}</div> </template> <script setup lang="ts"> import { computed, ref } from 'vue'; const numberRef = ref(0) // ===> Ref<number> const numberComputed = computed(() => numberRef.value) // ===> ComputedRef<number> interface IFoo {     bar: string } const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined> const fooComputed = computed(() => {     return fooRef.value }) // ===> ComputedRef<IFoo | undefined> </script> 复制代码

  • 可写的computed

<template>     <div>{{ fooComputedWritable?.bar }}</div> </template> <script setup lang="ts"> import { computed, ref } from 'vue'; interface IFoo {     bar: string } const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined> const fooComputedWritable = computed({     get: () => {         return fooRef.value     },     set: (value) => {         fooRef.value = value     } }) // ===> WritableComputedRef<IFoo | undefined> </script> 复制代码

使用watchwatchEffect

直接引入watchwatchEffect方法就可以使用了

<template>     <div>{{ numberRef }}</div> </template> <script setup lang="ts"> import { ref, watch, watchEffect } from 'vue'; const numberRef = ref(0) // ===> Ref<number> interface IFoo {     bar: string } const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined> watchEffect(() => console.log(fooRef.value?.bar)) watch(numberRef, () => {     console.log(`numberRef变化了`) }) const stop = watch(fooRef, () => {     console.log(`fooRef变化了`) }, {     deep: true }) // 检查深度嵌套的对象或数组 stop(); // 停止侦听 </script> 复制代码

使用nextTick

直接引入nextTick方法使用

<template>     <div></div> </template> <script setup lang="ts"> import { nextTick } from 'vue'; nextTick(() => {     // ... }) // 还可以使用 async/await async () => {     await nextTick()     // .... } </script> 复制代码

使用其他组件

直接引入其他的组件,然后在模板中使用就可以了,引入的组件会自动注册的。需要注意的是,使用动态组件的时候,应该使用动态的 :is 来绑定

<template>     <Foo></Foo>     <foo-item></foo-item>     <component :is="Foo" />     <component :is="someCondition ? Foo : FooItem" /> </template> <script setup lang="ts"> import Foo from "./Foo.vue" import FooItem from "./FooItem.vue" const someCondition = false </script> 复制代码

声明props

使用defineProps来声明props,分为不使用泛型的方式和使用泛型的方式,两者都可以同时声明类型和默认值

  • 非泛型方式:此时它接收和 props 选项相同的值。

<template>     <div>         <!-- 直接使用即可,不需要toRefs转换 -->         {{ foo }}     </div> </template> <script setup lang="ts"> import { toRefs } from 'vue'; interface ICustomType {     foo: string,     bar: string } const props = defineProps({     foo: String, // 使用构造函数声明类型     fooMultiTypes: [String, Number], // 多个类型     fooCustomType: Object as () => ICustomType, // 自定义类型     fooCustomTypeWithRequire: {         type: Object as () => ICustomType,         required: true     }, // 自定义类型,必选     fooCustomTypeWithDefault: {         type: Object as () => ICustomType,         default: () => {             return {                 foo: "foo",                 bar: "bar"             }         }     }, // 自定义类型,带默认值 }) // 1. 可以在模板<template>中使用声明的props,不需要用toRefs转换 // 2. 如果某一个值需要在setup中使用,则需要用toRefs转换下,然后把它解构出来 const {     foo,  // ===> Ref<string | undefined> | undefined     fooMultiTypes, // ===> Ref<string | number | undefined> | undefined     fooCustomType, // ===> Ref<ICustomType | undefined> | undefined     fooCustomTypeWithRequire, // ===> Ref<ICustomType> } = toRefs(props) </script> 复制代码

  • 泛型方式:此时声明默认值,需要 withDefaults 编译器宏

<template>     <div>         <!-- 直接使用即可,不需要toRefs转换 -->         {{ foo }}     </div> </template> <script setup lang="ts"> import { toRefs } from 'vue'; interface ICustomType {     foo: string,     bar: string } const props = defineProps<{     foo?: string,     fooWithRequire: string,     fooMultiTypes: string | number,     fooCustomType?: ICustomType,     fooCustomTypeWithRequire: ICustomType }>() // 泛型方式声明默认值,需要使用withDefaults 编译器宏 const propsWithDefault = withDefaults(     defineProps<{         fooCustomTypeWithDefault: ICustomType     }>(),     {         fooCustomTypeWithDefault: () => {             return {                 foo: "foo",                 bar: "bar"             }         }     }) // 1. 可以在模板<template>中使用声明的props,不需要用toRefs转换 // 2. 如果某一个值需要在setup中使用,则需要用toRefs转换下,然后把它解构出来 const {     foo,  // ===> Ref<string | undefined> | undefined     fooWithRequire,  // ===> Ref<string>     fooMultiTypes, // ===> Ref<string | number>     fooCustomType, // ===> Ref<ICustomType | undefined> | undefined     fooCustomTypeWithRequire, // ===> Ref<ICustomType> } = toRefs(props) const {     fooCustomTypeWithDefault,  // ===> Ref<ICustomType> } = toRefs(propsWithDefault) </script> 复制代码

声明emits

使用defineEmits来声明,它接收和 emits 选项相同的值。

<template>     <div @click="emitsWithObject('bar', $event)"></div> </template> <script setup lang="ts"> interface IUserInput {     email: string,     password: string } // 数组方式 const emitsWithArray = defineEmits(["foo", "bar"]) // 对象方式 const emitsWithObject = defineEmits({     // 带有验证函数,并且指定负载类型     foo: (payload: IUserInput) => {         if (payload.email && payload.password) {             return true         } else {             console.warn(`Invalid submit event payload!`)             return false         }     },     // 仅指定负载类型     bar: (evevnt: MouseEvent) => true }) // 泛型方式 const emitsWithGeneric = defineEmits<{     (e: 'foo', id: number): void     (e: 'bar', value: string): void }>() // 触发事件 emitsWithArray("foo") emitsWithObject("foo", { email: "bar@aa.com", password: 'bp' }) emitsWithGeneric("foo", 1) </script> 复制代码

递归组件

一个单文件组件可以通过它的文件名被其自己所引用,但是在有命名冲入的情况下,需要使用import 别名导入来避免冲突

// FooBar.vue <template>     <div>         <!-- 一个单文件组件可以通过它的文件名被其自己所引用 -->         <FooBar></FooBar>         <foo-bar></foo-bar>         <foo-bar-other></foo-bar-other>     </div> </template> <script setup lang="ts"> // 使用 import 别名导入避免冲突 import { default as FooBarOther } from './others/FooBar.vue' </script> 复制代码

生命周期钩子使用

直接引入对应的生命周期钩子函数使用即可

<template>     <div></div> </template> <script setup lang="ts"> import {     onBeforeMount,     onMounted,     onBeforeUpdate,     onUpdated,     onBeforeUnmount,     onUnmounted,     onErrorCaptured,     onRenderTracked,     onRenderTriggered,     onActivated,     onDeactivated } from "vue" // 直接使用就好了 onMounted(() => {     // ... }) </script> 复制代码

暴露组件属性

<script setup>中的变量默认是不会暴露出给其他组件的,使用defineExpose可以暴露需要其他组件可以访问的属性

  • Bar.vue中暴露属性,由于使用defineExpose暴露的属性类型暂时还没法被InstanceType<typeof Bar>这种方式提取出来有,具体的可以参照这个#4397,所以需要使用export手动的将暴露的类型导出。

<template>   <div></div> </template> <script setup lang="ts"> import { ref } from 'vue' const a = 1 const b = ref(2) const exposeValue = {   a,   b } defineExpose(exposeValue) export type IExposeType = typeof exposeValue </script> 复制代码

  • Foo.vue中通过模板引用使用暴露的属性

<template>     <bar ref="barRef"></bar> </template> <script setup lang="ts"> import { ref } from 'vue'; import Bar, { IExposeType } from './Bar.vue'; const barRef = ref<IExposeType>(); const a = barRef.value?.a  // number | undefined const b = barRef.value?.b  // Ref<number> | undefined </script> 复制代码

使用provide和inject

provideinject支持通过泛型来控制注入的变量的类型

  • Foo.vue中使用provide提供需要注入的依赖

<template>     <div></div> </template> <script setup lang="ts"> import { provide } from 'vue'; export interface IUser {     name: string,     age: number } provide("name", "foo") provide<IUser>("user", {     name: "foo",     age: 23 }) </script> 复制代码

  • Bar.vue中使用inject引入注入的依赖

<template>     <div></div> </template> <script setup lang="ts"> import { inject } from 'vue'; import { IUser } from './Foo.vue'; const name = inject<string>("name") // ===> string | undefined const user = inject<IUser>("user") // ===> IUser | undefined </script>


作者:LiuWenXing1996
链接:https://juejin.cn/post/7031565983269519367


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐