阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 与Vue集成

与Vue集成

作者:陈川 阅读数:61760人阅读 分类: TypeScript

为什么选择Vue与TypeScript结合

Vue的响应式系统与TypeScript的类型检查能形成互补。TypeScript为Vue组件提供了更严格的类型约束,特别是在处理复杂状态逻辑时,类型系统可以提前捕获潜在错误。Vue 3的Composition API设计对TypeScript支持更加友好,比如refreactive都能自动推断出包装值的类型。

// 自动推断为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后端开发

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌