响应式与TypeScript集成
响应式与TypeScript集成
Vue.js的响应式系统是其核心特性之一,结合TypeScript的类型检查能力,可以显著提升代码的可维护性和开发体验。通过类型定义和接口约束,开发者能够更清晰地描述数据结构和组件行为,减少运行时错误。
响应式基础与类型定义
Vue 3的reactive
和ref
函数可以直接与TypeScript类型系统集成。为响应式变量添加类型注解时,推荐使用泛型语法:
import { ref, reactive } from 'vue'
// 基本类型使用ref
const count = ref<number>(0) // 显式指定number类型
// 对象类型使用reactive
interface User {
id: number
name: string
age?: number // 可选属性
}
const user = reactive<User>({
id: 1,
name: 'Alice'
})
当访问这些响应式变量时,TypeScript会自动提供类型提示和检查:
user.name.toLowerCase() // 正确
user.age?.toFixed(2) // 正确处理可选属性
count.value = 'hello' // 类型错误:不能将string赋值给number
组件Props的类型安全
在组合式API中,使用defineProps
宏可以同时实现运行时声明和类型推导:
<script setup lang="ts">
const props = defineProps<{
title: string
list: Array<{ id: number; text: string }>
optional?: boolean
}>()
// 使用时获得完整类型提示
console.log(props.title.length)
</script>
对于更复杂的场景,可以分离接口定义:
interface Product {
sku: string
price: number
inventory: number
}
const props = defineProps<{
products: Product[]
maxVisible?: number
}>()
组合式函数类型实践
封装自定义hook时,返回值的类型需要特别注意:
import { ref, onMounted } from 'vue'
export function useFetch<T>(url: string) {
const data = ref<T | null>(null)
const error = ref<Error | null>(null)
onMounted(async () => {
try {
const response = await fetch(url)
data.value = await response.json()
} catch (err) {
error.value = err as Error
}
})
return { data, error }
}
使用时能获得完整的类型推断:
const { data, error } = useFetch<User[]>('/api/users')
// data自动推断为Ref<User[] | null>
if (data.value) {
const firstUser = data.value[0] // User类型
}
模板Ref的类型处理
模板引用需要通过泛型明确指定元素类型:
<script setup lang="ts">
import { ref } from 'vue'
const inputRef = ref<HTMLInputElement | null>(null)
function focusInput() {
inputRef.value?.focus() // 安全访问
}
</script>
<template>
<input ref="inputRef" />
<button @click="focusInput">Focus</button>
</template>
对于组件引用,可以使用InstanceType
获取组件实例类型:
import MyModal from './MyModal.vue'
const modalRef = ref<InstanceType<typeof MyModal> | null>(null)
function openModal() {
modalRef.value?.show()
}
事件处理的类型安全
自定义事件可以通过类型字面量进行约束:
const emit = defineEmits<{
(e: 'update', payload: number): void
(e: 'submit', payload: { values: string[] }): void
}>()
// 触发事件时会进行类型检查
emit('update', 42) // 正确
emit('submit', { values: ['a', 'b'] }) // 正确
emit('update', 'text') // 错误:参数类型不匹配
状态管理集成
Pinia与TypeScript的集成非常直观:
// stores/user.ts
import { defineStore } from 'pinia'
interface UserState {
list: User[]
current: User | null
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
list: [],
current: null
}),
actions: {
async fetchUsers() {
const res = await api.get<User[]>('/users')
this.list = res.data
}
}
})
组件中使用时保持类型安全:
const store = useUserStore()
store.list[0]?.name // 正确推断出User类型
高级类型模式
利用TypeScript的高级特性可以创建更精确的类型:
type UnwrapRef<T> = T extends Ref<infer U> ? U : T
function useDebouncedRef<T>(value: T, delay = 200) {
const timeout = ref<NodeJS.Timeout>()
const debouncedValue = ref<T>(value)
watch(() => value, (newVal) => {
clearTimeout(timeout.value)
timeout.value = setTimeout(() => {
debouncedValue.value = newVal as UnwrapRef<T>
}, delay)
})
return debouncedValue
}
类型扩展与全局声明
扩展全局组件类型声明:
// components.d.ts
declare module 'vue' {
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
BaseButton: typeof import('./components/BaseButton.vue')['default']
}
}
自定义指令类型:
// directives.d.ts
import { Directive } from 'vue'
declare module 'vue' {
interface ComponentCustomProperties {
vFocus: Directive<HTMLInputElement>
}
}
类型测试与验证
使用asserts
关键字创建类型断言函数:
function assertIsDefined<T>(val: T): asserts val is NonNullable<T> {
if (val === undefined || val === null) {
throw new Error(`Expected defined but got ${val}`)
}
}
const maybeUser = ref<User | null>(null)
// 使用断言后TypeScript会收窄类型
assertIsDefined(maybeUser.value)
console.log(maybeUser.value.id) // 安全访问
性能与类型优化
对于大型响应式对象,可以使用shallowRef
和markRaw
优化性能:
import { shallowRef, markRaw } from 'vue'
const heavyObject = markRaw({
/* 大型不可变数据 */
})
const optimizedRef = shallowRef(heavyObject) // 不会深度响应
类型工具函数
创建响应式转换工具类型:
type Reactive<T> = {
[K in keyof T]: T[K] extends object ? Reactive<T[K]> : Ref<T[K]>
}
function createReactive<T extends object>(obj: T): Reactive<T> {
return reactive(obj) as Reactive<T>
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:前端性能优化
下一篇:Vue Router4主要变化