<返回目录     Powered by claud/xia兄

第15课: 实战项目

Vue 3 + TypeScript项目

项目初始化

# 使用Vite创建项目
npm create vite@latest my-vue-app -- --template vue-ts

# 安装依赖
cd my-vue-app
npm install

# 安装Vue Router和Pinia
npm install vue-router@4 pinia

类型定义

// src/types/user.ts
export interface User {
  id: number;
  name: string;
  email: string;
  avatar?: string;
}

export interface LoginForm {
  email: string;
  password: string;
}

export interface ApiResponse {
  data: T;
  message: string;
  code: number;
}

API服务

// src/api/user.ts
import type { User, LoginForm, ApiResponse } from '@/types/user';

export const userApi = {
  async login(form: LoginForm): Promise> {
    const response = await fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(form)
    });
    return response.json();
  },

  async getUser(id: number): Promise> {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
  }
};

Pinia Store

// src/stores/user.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import type { User } from '@/types/user';

export const useUserStore = defineStore('user', () => {
  const user = ref(null);
  const isLoggedIn = computed(() => user.value !== null);

  function setUser(newUser: User) {
    user.value = newUser;
  }

  function logout() {
    user.value = null;
  }

  return { user, isLoggedIn, setUser, logout };
});

Vue组件

// src/components/UserCard.vue
<script setup lang="ts">
import type { User } from '@/types/user';

interface Props {
  user: User;
  showEmail?: boolean;
}

const props = withDefaults(defineProps(), {
  showEmail: true
});

const emit = defineEmits<{
  click: [user: User];
}>();

function handleClick() {
  emit('click', props.user);
}
</script>

<template>
  <div class="user-card" @click="handleClick">
    <img :src="user.avatar" :alt="user.name" />
    <h3>{{ user.name }}</h3>
    <p v-if="showEmail">{{ user.email }}</p>
  </div>
</template>

React + TypeScript项目

项目初始化

# 使用Vite创建项目
npm create vite@latest my-react-app -- --template react-ts

# 安装依赖
cd my-react-app
npm install

类型定义

// src/types/index.ts
export interface Todo {
  id: number;
  title: string;
  completed: boolean;
  createdAt: Date;
}

export type TodoFilter = 'all' | 'active' | 'completed';

自定义Hook

// src/hooks/useTodos.ts
import { useState, useCallback } from 'react';
import type { Todo } from '@/types';

export function useTodos() {
  const [todos, setTodos] = useState([]);

  const addTodo = useCallback((title: string) => {
    const newTodo: Todo = {
      id: Date.now(),
      title,
      completed: false,
      createdAt: new Date()
    };
    setTodos(prev => [...prev, newTodo]);
  }, []);

  const toggleTodo = useCallback((id: number) => {
    setTodos(prev =>
      prev.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  }, []);

  const deleteTodo = useCallback((id: number) => {
    setTodos(prev => prev.filter(todo => todo.id !== id));
  }, []);

  return { todos, addTodo, toggleTodo, deleteTodo };
}

React组件

// src/components/TodoItem.tsx
import React from 'react';
import type { Todo } from '@/types';

interface TodoItemProps {
  todo: Todo;
  onToggle: (id: number) => void;
  onDelete: (id: number) => void;
}

export const TodoItem: React.FC = ({
  todo,
  onToggle,
  onDelete
}) => {
  return (
    <div className="todo-item">
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => onToggle(todo.id)}
      />
      <span className={todo.completed ? 'completed' : ''}>
        {todo.title}
      </span>
      <button onClick={() => onDelete(todo.id)}>Delete</button>
    </div>
  );
};

Node.js + TypeScript项目

项目初始化

# 初始化项目
npm init -y

# 安装依赖
npm install express
npm install -D typescript @types/node @types/express ts-node nodemon

# 初始化TypeScript配置
npx tsc --init

Express服务器

// src/index.ts
import express, { Request, Response, NextFunction } from 'express';

const app = express();
const PORT = 3000;

app.use(express.json());

// 类型定义
interface User {
  id: number;
  name: string;
  email: string;
}

// 扩展Request类型
interface TypedRequest extends Request {
  body: T;
}

// 路由处理
app.get('/api/users', (req: Request, res: Response) => {
  const users: User[] = [
    { id: 1, name: 'Alice', email: 'alice@example.com' }
  ];
  res.json(users);
});

app.post('/api/users', (req: TypedRequest, res: Response) => {
  const newUser = req.body;
  res.status(201).json(newUser);
});

// 错误处理中间件
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Internal Server Error' });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

数据库模型

// src/models/User.ts
export class User {
  constructor(
    public id: number,
    public name: string,
    public email: string,
    private password: string
  ) {}

  public validatePassword(password: string): boolean {
    return this.password === password;
  }

  public toJSON() {
    return {
      id: this.id,
      name: this.name,
      email: this.email
    };
  }
}

TypeScript最佳实践

1. 避免使用any

// 不好
function process(data: any) {
  return data.value;
}

// 好
function process(data: T) {
  return data.value;
}

2. 使用类型守卫

function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function process(value: unknown) {
  if (isString(value)) {
    console.log(value.toUpperCase());
  }
}

3. 使用readonly

interface Config {
  readonly apiUrl: string;
  readonly timeout: number;
}

const config: Readonly = {
  apiUrl: 'https://api.example.com',
  timeout: 5000
};

4. 使用联合类型而不是枚举

// 推荐
type Status = 'pending' | 'success' | 'error';

// 而不是
enum Status {
  Pending = 'pending',
  Success = 'success',
  Error = 'error'
}

5. 使用工具类型

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// 创建用户时不需要id
type CreateUserDTO = Omit;

// 更新用户时所有字段可选
type UpdateUserDTO = Partial;

// 公开用户信息
type PublicUser = Omit;
项目开发流程:
  1. 设计类型系统和接口定义
  2. 配置TypeScript和构建工具
  3. 实现核心功能和业务逻辑
  4. 编写单元测试和集成测试
  5. 配置代码检查和格式化
  6. 优化性能和打包配置
  7. 部署和持续集成
实战练习:
  1. 创建一个Todo应用,使用Vue 3或React + TypeScript
  2. 实现用户认证系统,包含登录、注册、权限管理
  3. 构建RESTful API服务,使用Node.js + Express + TypeScript
  4. 集成数据库(如MongoDB或PostgreSQL)并定义类型
  5. 添加单元测试和端到端测试
  6. 配置CI/CD流程

学习资源