TypeScript + Vue 3 实战:现代前端开发最佳实践
TypeScript + Vue 3 实战:现代前端开发最佳实践
在参与 Com6102gp5 团队项目的过程中,我深入实践了 TypeScript 和 Vue 3 的组合开发。这篇文章分享一些实战经验和最佳实践。
🎯 为什么选择 TypeScript + Vue 3?
TypeScript 的优势
- 类型安全:编译时捕获错误,减少运行时 bug
- 智能提示:IDE 提供更好的代码补全和提示
- 代码可维护性:类型定义即文档,易于理解和维护
- 重构友好:重命名、查找引用等操作更安全
Vue 3 的改进
- Composition API:更灵活的逻辑组织方式
- 性能提升:更快的渲染和更小的包体积
- TypeScript 支持:原生 TypeScript 支持,类型推导更完善
- Teleport / Suspense:新的内置组件
🛠️ 项目搭建
使用 Vite 创建项目
1 2 3 4 5 6
| npm create vite@latest my-vue-app -- --template vue-ts
cd my-vue-app npm install npm run dev
|
推荐的项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13
| src/ ├── assets/ # 静态资源 ├── components/ # 公共组件 │ ├── common/ # 基础组件 │ └── business/ # 业务组件 ├── composables/ # 组合式函数 ├── router/ # 路由配置 ├── stores/ # 状态管理 (Pinia) ├── types/ # TypeScript 类型定义 ├── utils/ # 工具函数 ├── views/ # 页面组件 ├── App.vue └── main.ts
|
💡 核心实践
1. 使用 Composition API
传统 Options API:
1 2 3 4 5 6 7 8 9 10 11 12
| export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } } }
|
Composition API(推荐):
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { ref } from 'vue'
export default { setup() { const count = ref(0) const increment = () => { count.value++ } return { count, increment } } }
|
使用 <script setup>(最推荐):
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script setup lang="ts"> import { ref } from 'vue'
const count = ref(0)
const increment = () => { count.value++ } </script>
<template> <button @click="increment">Count: {{ count }}</button> </template>
|
2. TypeScript 类型定义
定义接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export interface User { id: number name: string email: string avatar?: string role: 'admin' | 'user' | 'guest' }
export interface ApiResponse<T> { code: number message: string data: T }
|
在组件中使用:
1 2 3 4 5 6 7 8 9 10 11 12
| <script setup lang="ts"> import { ref } from 'vue' import type { User } from '@/types/user'
const user = ref<User | null>(null)
const fetchUser = async (id: number): Promise<void> => { const response = await fetch(`/api/users/${id}`) const data: ApiResponse<User> = await response.json() user.value = data.data } </script>
|
3. 组合式函数(Composables)
创建可复用逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { ref, onMounted, onUnmounted } from 'vue'
export function useMouse() { const x = ref(0) const y = ref(0) const update = (event: MouseEvent) => { x.value = event.pageX y.value = event.pageY } onMounted(() => { window.addEventListener('mousemove', update) }) onUnmounted(() => { window.removeEventListener('mousemove', update) }) return { x, y } }
|
在组件中使用:
1 2 3 4 5 6 7 8 9
| <script setup lang="ts"> import { useMouse } from '@/composables/useMouse'
const { x, y } = useMouse() </script>
<template> <div>Mouse position: {{ x }}, {{ y }}</div> </template>
|
4. 使用 Pinia 进行状态管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import { defineStore } from 'pinia' import type { User } from '@/types/user'
export const useUserStore = defineStore('user', { state: () => ({ currentUser: null as User | null, isLoggedIn: false }), getters: { userName: (state) => state.currentUser?.name ?? 'Guest', isAdmin: (state) => state.currentUser?.role === 'admin' }, actions: { async login(email: string, password: string) { try { const response = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ email, password }) }) const data = await response.json() this.currentUser = data.user this.isLoggedIn = true } catch (error) { console.error('Login failed:', error) throw error } }, logout() { this.currentUser = null this.isLoggedIn = false } } })
|
5. 组件通信
Props 和 Emits:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <script setup lang="ts"> interface Props { title: string count?: number }
interface Emits { (e: 'update', value: number): void (e: 'delete'): void }
const props = withDefaults(defineProps<Props>(), { count: 0 })
const emit = defineEmits<Emits>()
const handleUpdate = () => { emit('update', props.count + 1) } </script>
<template> <div> <h2>{{ title }}</h2> <p>Count: {{ count }}</p> <button @click="handleUpdate">Update</button> <button @click="emit('delete')">Delete</button> </div> </template>
|
6. Vue Router 类型安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import { createRouter, createWebHistory } from 'vue-router' import type { RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [ { path: '/', name: 'Home', component: () => import('@/views/Home.vue') }, { path: '/user/:id', name: 'UserProfile', component: () => import('@/views/UserProfile.vue'), props: true } ]
const router = createRouter({ history: createWebHistory(), routes })
router.beforeEach((to, from, next) => { const userStore = useUserStore() if (to.meta.requiresAuth && !userStore.isLoggedIn) { next('/login') } else { next() } })
export default router
|
🎨 UI 组件库集成
推荐的组件库
- Element Plus:企业级应用
- Ant Design Vue:阿里巴巴设计体系
- Naive UI:完整的 TypeScript 支持
- Vuetify:Material Design 风格
Element Plus 示例
1
| npm install element-plus
|
1 2 3 4 5 6 7 8 9
| import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue'
const app = createApp(App) app.use(ElementPlus) app.mount('#app')
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script setup lang="ts"> import { ElMessage } from 'element-plus'
const handleSubmit = () => { ElMessage.success('提交成功!') } </script>
<template> <el-form> <el-form-item label="用户名"> <el-input v-model="username" /> </el-form-item> <el-button type="primary" @click="handleSubmit">提交</el-button> </el-form> </template>
|
🔧 开发工具配置
VSCode 推荐插件
- Vue Language Features (Volar):Vue 3 官方推荐
- TypeScript Vue Plugin (Volar):TS 支持
- ESLint:代码检查
- Prettier:代码格式化
ESLint + Prettier 配置
1 2 3 4 5 6 7 8 9 10 11 12
| module.exports = { extends: [ 'plugin:vue/vue3-recommended', '@vue/typescript/recommended', 'prettier' ], rules: { '@typescript-eslint/no-unused-vars': 'warn', 'vue/multi-word-component-names': 'off' } }
|
1 2 3 4 5 6 7
| { "semi": false, "singleQuote": true, "trailingComma": "none", "printWidth": 100 }
|
🚀 性能优化
1. 懒加载路由
1 2 3 4 5 6
| const routes = [ { path: '/about', component: () => import('@/views/About.vue') } ]
|
2. 使用 defineAsyncComponent
1 2 3 4 5
| import { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent(() => import('@/components/HeavyComponent.vue') )
|
3. 合理使用 v-memo
1 2 3 4 5
| <template> <div v-for="item in list" :key="item.id" v-memo="[item.id]"> {{ item.name }} </div> </template>
|
📦 项目实战:Com6102gp5
在 Com6102gp5 项目中,我们:
- 采用 Monorepo 结构:前后端代码在同一仓库
- 使用 Vite:快速的开发体验
- 集成 CI/CD:自动化测试和部署
- 代码规范:统一的 ESLint 和 Prettier 配置
- 类型安全:全项目 TypeScript
团队协作经验
- 📋 使用 Git Flow 工作流
- 🔍 强制 Code Review
- 🧪 编写单元测试
- 📝 维护完善的文档
💭 最佳实践总结
- ✅ 始终使用 TypeScript,不要用
any
- ✅ 优先使用 Composition API 和
<script setup>
- ✅ 提取可复用逻辑到 composables
- ✅ 合理拆分组件,保持单一职责
- ✅ 使用 Pinia 管理全局状态
- ✅ 配置 ESLint 和 Prettier
- ✅ 编写必要的单元测试
🔗 资源链接
TypeScript 和 Vue 3 的组合为现代前端开发提供了强大的工具链。虽然有学习曲线,但带来的类型安全和开发体验提升是值得的。