vue3与ts的组合式API

vue3与ts的组合式API

props

1.基本props
vue 复制代码
<script setup lang="ts">
const props = defineProps<{
  foo: string
  bar?: number
}>()
</script>
2.抽离props
vue 复制代码
<script setup lang="ts">
interface Props {
  foo: string
  bar?: number
}

const props = defineProps<Props>()
</script>
3.抽离和导入type
vue 复制代码
<script setup lang="ts">
import type { Props } from './foo'

const props = defineProps<Props>()
</script>
4.默认值props
tsx 复制代码
export interface Props {
  msg?: string
  labels?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})
5.复杂props
vue 复制代码
<script setup lang="ts">
interface Book {
  title: string
  author: string
  year: number
}

const props = defineProps<{
  book: Book
}>()
</script>

emits

一个类型字面量,其中键是事件名称,值是数组或元组类型,表示事件的附加接受参数。示例使用了具名元组,因此每个参数都可以有一个显式的名称。

vue 复制代码
<script setup lang="ts">
// 基于类型
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()

// 3.3+: 可选的、更简洁的语法
const emit = defineEmits<{
  change: [id: number]
  update: [value: string]
}>()
</script>

ref()

1.自动推导
typescript 复制代码
import { ref } from 'vue'

// 推导出的类型:Ref<number>
const year = ref(2020)

// => TS Error: Type 'string' is not assignable to type 'number'.
year.value = '2020'
2.复杂ref
typescript 复制代码
import { ref } from 'vue'
import type { Ref } from 'vue'

const year: Ref<string | number> = ref('2020')

year.value = 2020 // 成功!

个人建议使用第二种,字少了,且无需导入import type { Ref } from 'vue'

typescript 复制代码
// 得到的类型:Ref<string | number>
const year = ref<string | number>('2020')

year.value = 2020 // 成功!

注意:如果你指定了一个泛型参数但没有给出初始值 ,那么最后得到的就将是一个包含 undefined 的联合类型:

typescript 复制代码
// 推导得到的类型:Ref<number | undefined>
const n = ref<number>()

recative()

1.自动推导
typescript 复制代码
import { reactive } from 'vue'

// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue 3 指引' })
2.复杂reactive
typescript 复制代码
import { reactive } from 'vue'

interface Book {
  title: string
  year?: number
}

const book: Book = reactive({ title: 'Vue 3 指引' })

注意: 不推荐使用 reactive()泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。

错误示例:

typescript 复制代码
import { reactive } from 'vue'

interface Book {
  title: string;
  year?: number;
}

// 不推荐:使用了泛型参数,但返回的类型与Book不同
const book: Book = reactive<Book>({ title: 'Vue 3 指引' });

正确示例,使用toRefs()

typescript 复制代码
import { reactive, toRefs } from 'vue'

interface Book {
  title: string;
  year?: number;
}

// 创建一个响应式的Book对象
const bookReactive = reactive({ title: 'Vue 3 指引' });

// 使用toRefs()将每个属性转换为Ref,确保类型安全
const bookRefs = toRefs(bookReactive);

// 现在bookRefs是一个Record类型,其中的属性具有与Book相同的类型
const book: { title: Ref<string>; year?: Ref<number | undefined> } = bookRefs;

// 或者,如果你知道你只会在模板中使用,可以使用类型断言
const bookWithAssertion = {
  title: bookRefs.title as Ref<string>,
  year: bookRefs.year as Ref<number | undefined>,
} as const;

computed()

1.自动推导
typescript 复制代码
import { ref, computed } from 'vue'

const count = ref(0)

// 推导得到的类型:ComputedRef<number>
const double = computed(() => count.value * 2)

// => TS Error: Property 'split' does not exist on type 'number'
const result = double.value.split('')
2.显式指定
typescript 复制代码
const double = computed<number>(() => {
  // 若返回值不是 number 类型则会报错
})

为事件处理函数标注类型

**在处理原生 DOM 事件时,应该为我们传递给事件处理函数的参数正确地标注类型。**让我们看一下这个例子:

typescript 复制代码
<script setup lang="ts">
function handleChange(event) {
  // `event` 隐式地标注为 `any` 类型
  console.log(event.target.value)
}
</script>

<template>
  <input type="text" @change="handleChange" />
</template>

没有类型标注时,这个 event 参数会隐式地标注为 any 类型。这也会在 tsconfig.json 中配置了 "strict": true"noImplicitAny": true 时报出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,你在访问 event 上的属性时可能需要使用类型断言:

typescript 复制代码
function handleChange(event: Event) {
  console.log((event.target as HTMLInputElement).value)
}

为 provide / inject 标注类型

typescript 复制代码
const foo = inject<string>('foo') // 类型:string | undefined ???

为模板引用标注类型

模板引用需要通过一个显式指定的泛型参数和一个初始值 null 来创建:

typescript 复制代码
<script setup lang="ts">
import { ref, onMounted } from 'vue'

const el = ref<HTMLInputElement | null>(null)

onMounted(() => {
  el.value?.focus()
})
</script>

<template>
  <input ref="el" />
</template>

可以通过类似于 MDN 的页面来获取正确的 DOM 接口。https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLInputElement

注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null

为组件模板引用标注类型

有时,你可能需要为一个子组件添加一个模板引用,以便调用它公开的方法。举例来说,我们有一个 MyModal 子组件,它有一个打开模态框的方法:

vue 复制代码
<!-- MyModal.vue -->
<script setup lang="ts">
import { ref } from 'vue'

const isContentShown = ref(false)
const open = () => (isContentShown.value = true)

defineExpose({
  open
})
</script>

为了获取 MyModal 的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:

vue 复制代码
<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue'

const modal = ref<InstanceType<typeof MyModal> | null>(null)

const openModal = () => {
  modal.value?.open()
}
</script>

如果组件的具体类型无法获得,或者你并不关心组件的具体类型,那么可以使用 ComponentPublicInstance。这只会包含所有组件都共享的属性,比如 $el

typescript 复制代码
import { ref } from 'vue'
import type { ComponentPublicInstance } from 'vue'

const child = ref<ComponentPublicInstance | null>(null)

官网链接

相关推荐
牧羊人_myr1 天前
Ajax 技术详解
前端
浩男孩1 天前
🍀封装个 Button 组件,使用 vitest 来测试一下
前端
蓝银草同学1 天前
阿里 Iconfont 项目丢失?手把手教你将已引用的 SVG 图标下载到本地
前端·icon
布列瑟农的星空1 天前
重学React —— React事件机制 vs 浏览器事件机制
前端
程序定小飞1 天前
基于springboot的在线商城系统设计与开发
java·数据库·vue.js·spring boot·后端
一小池勺1 天前
CommonJS
前端·面试
孙牛牛1 天前
实战分享:一招解决嵌套依赖版本失控问题,以 undici 为例
前端
用户52709648744901 天前
Git 最佳实践
前端
星秀日1 天前
JavaWeb--Ajax
前端·javascript·ajax