与Vue集成
为什么选择Vue与TypeScript结合
Vue的响应式系统与TypeScript的类型检查能形成互补。TypeScript为Vue组件提供了更严格的类型约束,特别是在处理复杂状态逻辑时,类型系统可以提前捕获潜在错误。Vue 3的Composition API设计对TypeScript支持更加友好,比如ref
和reactive
都能自动推断出包装值的类型。
// 自动推断为Ref<number>
const count = ref(0)
interface User {
name: string
age: number
}
// 明确声明响应式对象类型
const user = reactive<User>({
name: 'Alice',
age: 25
})
项目初始配置
使用Vite创建项目时选择vue-ts
模板是最快捷的方式:
npm create vite@latest my-vue-app -- --template vue-ts
关键配置项需要关注tsconfig.json
中的这些参数:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
组件类型定义实践
单文件组件中可以使用<script setup lang="ts">
语法获得最佳类型支持。Props的类型定义有两种主流方式:
// 方式1:使用运行时声明
const props = defineProps({
title: {
type: String,
required: true
},
likes: Number
})
// 方式2:基于类型的声明
interface Props {
title: string
likes?: number
}
const props = defineProps<Props>()
事件发射器的类型化示例:
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
(e: 'submit', payload: { email: string; password: string }): void
}>()
// 使用时会有类型校验
emit('submit', {
email: 'test@example.com',
password: '123456'
})
组合式API的类型增强
在setup函数中使用reactive时,推荐使用接口明确定义状态结构:
interface AppState {
loading: boolean
data: Item[] | null
error: Error | null
}
const state = reactive<AppState>({
loading: false,
data: null,
error: null
})
自定义组合函数需要显式声明返回类型:
import { ref, onMounted, Ref } from 'vue'
export function useFetch<T>(url: string): {
data: Ref<T | null>
error: Ref<Error | null>
} {
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 }
}
第三方库的类型扩展
为Vue插件添加类型声明示例:
// types/vue.d.ts
import { Plugin } from 'vue'
declare module 'vue' {
interface ComponentCustomProperties {
$filters: {
currency(value: number): string
}
}
}
const plugin: Plugin = {
install(app) {
app.config.globalProperties.$filters = {
currency(value: number) {
return '$' + value.toFixed(2)
}
}
}
}
处理Axios响应数据的类型守卫:
interface ApiResponse<T> {
code: number
data: T
message: string
}
function isApiResponse<T>(obj: any): obj is ApiResponse<T> {
return 'code' in obj && 'data' in obj && 'message' in obj
}
async function fetchData<T>(url: string): Promise<T> {
const response = await axios.get(url)
if (isApiResponse<T>(response.data)) {
return response.data.data
}
throw new Error('Invalid API response structure')
}
与Vuex/Pinia的类型集成
Pinia天然支持TypeScript,定义store时的最佳实践:
// stores/user.ts
import { defineStore } from 'pinia'
interface UserState {
name: string
permissions: string[]
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
name: '',
permissions: []
}),
actions: {
async login(credentials: { email: string; password: string }) {
// 实现登录逻辑
}
}
})
在组件中使用时能获得完整的类型提示:
const store = useUserStore()
store.name // 类型为string
store.login({ email: 'test@example.com', password: 'secret' }) // 参数类型校验
高级类型模式应用
实现类型安全的provide/inject:
// types/keys.ts
import type { InjectionKey } from 'vue'
export interface UserContext {
id: number
roles: string[]
}
export const userInjectionKey = Symbol() as InjectionKey<UserContext>
// 父组件
const userData: UserContext = { id: 1, roles: ['admin'] }
provide(userInjectionKey, userData)
// 子组件
const userContext = inject(userInjectionKey)
if (!userContext) throw new Error('Missing provider')
console.log(userContext.roles) // 安全访问
基于泛型的表单验证组件示例:
interface ValidationRule<T> {
validator: (value: T) => boolean
message: string
}
function useFormValidator<T>(initialValue: T, rules: ValidationRule<T>[]) {
const value = ref<T>(initialValue)
const errors = ref<string[]>([])
function validate() {
errors.value = rules
.filter(rule => !rule.validator(value.value))
.map(rule => rule.message)
return errors.value.length === 0
}
return { value, errors, validate }
}
// 使用示例
const { value: username, errors } = useFormValidator('', [
{
validator: (val: string) => val.length >= 3,
message: '至少3个字符'
}
])
测试中的类型应用
组件测试时的类型化示例:
import { mount } from '@vue/test-utils'
import MyComponent from './MyComponent.vue'
test('emits submit event', async () => {
const wrapper = mount(MyComponent)
await wrapper.find('form').trigger('submit.prevent')
// 类型安全的断言
expect(wrapper.emitted()).toHaveProperty('submit')
const emittedEvent = wrapper.emitted('submit')![0]
expect(emittedEvent[0]).toEqual({
email: expect.any(String),
password: expect.any(String)
})
})
模拟API请求的类型化方案:
import axios from 'axios'
import { vi } from 'vitest'
interface MockResponse<T = any> {
data: T
status: number
}
function mockAxiosResponse<T>(response: MockResponse<T>) {
vi.spyOn(axios, 'get').mockResolvedValue(response)
}
// 使用示例
mockAxiosResponse<User>({
data: { id: 1, name: 'Mock User' },
status: 200
})
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:与React集成
下一篇:与Node.js后端开发