返回 Skill 列表
extension
分类: 开发与工程无需 API Key

框架skills1

这是一个自定义skills

person作者: dadawd12hubModelScope

IoCent 1.0.0 — Code Wiki

目录


1. 项目概述

IoCent 是一个基于 Node.js 的 AI 对话应用,提供流畅、美观的智能聊天体验。用户可以与 AI 进行多轮对话,支持流式输出(SSE)、对话历史管理、Markdown 渲染与代码高亮。

| 属性 | 说明 | |------|------| | 项目名称 | iocent1.0.0 | | 版本 | 0.0.0 (初始版本) | | 模块类型 | ESM ("type": "module") | | 目标用户 | 需要 AI 辅助对话的开发者、创作者和普通用户 | | 核心价值 | 开箱即用的本地 AI 对话体验,界面精致,交互流畅 |


2. 整体架构

项目采用 前后端分离 的全栈架构,前端与后端在同一仓库中协同开发:

┌─────────────────────────────────────────────────────────┐
│                      前端层 (Client)                      │
│  React 18 + TypeScript + Vite + TailwindCSS + Zustand   │
│                                                          │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────┐ │
│  │  Pages   │  │Components│  │  Store   │  │  Hooks  │ │
│  │  (Home)  │  │(Chat/Nav)│  │(Zustand) │  │(Theme)  │ │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └─────────┘ │
│       └──────────────┼─────────────┘                     │
│                      │ fetch / SSE                       │
└──────────────────────┼───────────────────────────────────┘
                       │ /api/*
┌──────────────────────┼───────────────────────────────────┐
│                      ▼        后端层 (Server)             │
│              Express.js + TypeScript (ESM)               │
│                                                          │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────┐ │
│  │   Auth   │  │   Chat   │  │Conversa- │  │ Models  │ │
│  │  Routes  │  │  Routes  │  │  tions   │  │ Routes  │ │
│  └──────────┘  └────┬─────┘  └────┬─────┘  └────┬────┘ │
│                     │             │              │       │
│              ┌──────┴──────┐      │              │       │
│              │MemoryStore  │      │              │       │
│              └──────┬──────┘      │              │       │
│                     │             │              │       │
└─────────────────────┼─────────────┼──────────────┼───────┘
                      │             │              │
                      ▼             ▼              ▼
              ┌─────────────────────────────────────────┐
              │         OpenAI 兼容 API (外部服务)        │
              │    (支持任意 OpenAI 兼容的 LLM 后端)       │
              └─────────────────────────────────────────┘

关键架构决策:

  • 前端通过 Vite 开发代理 (/apilocalhost:3001) 与后端通信,生产环境通过 Vercel rewrites 实现
  • 后端使用 SSE (Server-Sent Events) 实现流式响应,前端通过 ReadableStream 逐行解析
  • 数据存储采用内存 Map,服务重启后数据丢失(适合演示/开发阶段)
  • OpenAI 客户端采用懒初始化单例模式,支持自定义 baseURL,兼容各类 OpenAI API 格式的后端

3. 目录结构

iocent1.0.0/
├── api/                          # 后端 API 服务
│   ├── routes/                   # API 路由模块
│   │   ├── auth.ts               # 认证路由(占位,未实现)
│   │   ├── chat.ts               # AI 对话核心路由(SSE 流式)
│   │   ├── conversations.ts      # 对话 CRUD 路由
│   │   └── models.ts             # 模型列表路由
│   ├── store/
│   │   └── memoryStore.ts        # 内存数据存储层
│   ├── app.ts                    # Express 应用配置
│   ├── index.ts                  # Vercel Serverless 入口
│   └── server.ts                 # 本地开发服务器入口
├── src/                          # 前端源码
│   ├── assets/                   # 静态资源
│   │   └── react.svg
│   ├── components/               # UI 组件
│   │   ├── ChatInput.tsx         # 消息输入框
│   │   ├── ChatMessage.tsx       # 消息气泡(Markdown 渲染)
│   │   ├── Empty.tsx             # 空状态占位组件
│   │   ├── ModelSelector.tsx     # 模型选择下拉框
│   │   └── Sidebar.tsx           # 侧边栏(对话列表+模型选择)
│   ├── hooks/
│   │   └── useTheme.ts           # 主题切换 Hook
│   ├── lib/
│   │   └── utils.ts              # 工具函数(cn 类名合并)
│   ├── pages/
│   │   └── Home.tsx              # 主页面
│   ├── store/
│   │   └── chatStore.ts          # Zustand 全局状态
│   ├── App.tsx                   # 应用根组件(路由配置)
│   ├── index.css                 # 全局样式 + Tailwind
│   ├── main.tsx                  # 前端入口
│   └── vite-env.d.ts             # Vite 类型声明
├── public/
│   └── favicon.svg               # 网站图标
├── dist/                         # 构建产物
├── .env                          # 环境变量配置
├── .gitignore
├── eslint.config.js              # ESLint 配置
├── index.html                    # HTML 入口
├── nodemon.json                  # Nodemon 开发配置
├── package.json                  # 项目依赖与脚本
├── postcss.config.js             # PostCSS 配置
├── tailwind.config.js            # TailwindCSS 配置
├── tsconfig.json                 # TypeScript 配置
├── vercel.json                   # Vercel 部署配置
└── vite.config.ts                # Vite 构建配置

4. 技术栈与依赖

4.1 核心技术栈

| 层级 | 技术 | 版本 | 用途 | |------|------|------|------| | 前端框架 | React | ^18.3.1 | UI 构建 | | 前端语言 | TypeScript | ~5.8.3 | 类型安全 | | 构建工具 | Vite | ^6.3.5 | 开发服务器 + 打包 | | CSS 框架 | TailwindCSS | ^3.4.17 | 原子化样式 | | 状态管理 | Zustand | ^5.0.3 | 全局状态 | | 路由 | React Router DOM | ^7.3.0 | 前端路由 | | 后端框架 | Express | ^4.21.2 | HTTP 服务 | | AI SDK | OpenAI | ^6.39.1 | 调用 LLM API |

4.2 运行时依赖

| 包名 | 版本 | 用途 | |------|------|------| | clsx | ^2.1.1 | 条件类名拼接 | | cors | ^2.8.5 | 跨域资源共享中间件 | | dotenv | ^17.2.1 | 环境变量加载 | | lucide-react | ^0.511.0 | 图标库 | | react-markdown | ^10.1.0 | Markdown 渲染 | | react-syntax-highlighter | ^16.1.1 | 代码语法高亮 | | remark-gfm | ^4.0.1 | GitHub Flavored Markdown 支持 | | tailwind-merge | ^3.0.2 | Tailwind 类名智能合并 |

4.3 开发依赖

| 包名 | 版本 | 用途 | |------|------|------| | @vercel/node | ^5.3.6 | Vercel Serverless 类型支持 | | concurrently | ^9.2.0 | 并行运行前后端开发服务器 | | nodemon | ^3.1.10 | 后端热重载 | | tsx | ^4.20.3 | TypeScript 直接执行 | | vite-tsconfig-paths | ^5.1.4 | Vite 路径别名支持 | | autoprefixer | ^10.4.21 | CSS 自动前缀 | | postcss | ^8.5.3 | CSS 处理管线 |

4.4 依赖关系图

前端依赖链:
  React ← react-dom
       ← react-router-dom (路由)
       ← zustand (状态)
       ← lucide-react (图标)
       ← react-markdown ← remark-gfm
                        ← react-syntax-highlighter
       ← clsx + tailwind-merge → cn() 工具函数

后端依赖链:
  Express ← cors (跨域)
          ← dotenv (环境变量)
          ← openai (AI API 客户端)

5. 前端模块详解

5.1 入口与路由

main.tsx

前端应用入口文件,负责:

  • StrictMode 渲染 <App /> 根组件
  • 导入全局样式 index.css

App.tsx

应用根组件,配置前端路由:

| 路径 | 组件 | 说明 | |------|------|------| | / | Home | 主对话页面 | | /other | 占位组件 | 预留扩展页面 |

使用 BrowserRouter 管理路由,通过 @/pages/Home 路径别名导入。


5.2 页面组件

Home.tsx

主对话页面,是应用的核心页面。

职责:

  • 初始化加载对话列表和模型列表(useEffect
  • 管理活跃对话的消息展示
  • 提供空状态引导("开始对话"按钮)
  • 自动滚动到最新消息

关键状态(来自 chatStore):

| 状态 | 类型 | 说明 | |------|------|------| | conversations | Conversation[] | 所有对话列表 | | activeConversationId | string \| null | 当前活跃对话 ID | | isStreaming | boolean | 是否正在流式生成 |

布局结构:

┌─────────────────────────────────────┐
│ Sidebar │  Header (对话标题+状态)    │
│         │───────────────────────────│
│ 对话列表 │  Messages (消息列表)       │
│         │  ┌─ ChatMessage ─┐       │
│         │  └───────────────┘       │
│         │───────────────────────────│
│ 模型选择 │  ChatInput (输入框)       │
└─────────────────────────────────────┘

5.3 UI 组件

ChatInput.tsx

消息输入组件,固定在对话区底部。

功能:

  • 自适应高度的 <textarea>(最大 200px)
  • Enter 发送消息,Shift+Enter 换行
  • 流式生成中显示停止按钮(红色方块图标),点击调用 stopStreaming()
  • 非流式状态显示发送按钮(箭头图标)

关键交互:

| 操作 | 行为 | |------|------| | Enter | 发送消息(清空输入框,调用 sendMessage) | | Shift+Enter | 换行 | | 点击发送按钮 | 发送消息 | | 点击停止按钮 | 中止流式请求(AbortController.abort()) |

ChatMessage.tsx

单条消息气泡组件,支持 Markdown 渲染和代码高亮。

功能:

  • 用户消息右对齐(翠绿色边框),AI 消息左对齐(暗灰色背景)
  • 使用 react-markdown + remark-gfm 渲染 Markdown
  • 代码块使用 react-syntax-highlighter(Prism + vscDarkPlus 主题)高亮
  • 代码块顶部显示语言标签和复制按钮
  • 流式生成时显示闪烁光标(.streaming-cursor
  • AI 消息支持一键复制(hover 显示复制按钮)

Props:

| 属性 | 类型 | 说明 | |------|------|------| | message | Message | 消息对象 | | isStreaming | boolean | 是否正在流式生成 |

Sidebar.tsx

侧边栏组件,包含对话列表和模型选择。

功能:

  • 显示应用标题 "IoCent"
  • 新建对话按钮
  • 对话列表(点击切换,hover 显示删除按钮)
  • 活跃对话高亮(翠绿指示条 + 背景)
  • 侧边栏折叠/展开(移动端隐藏,桌面端可切换)
  • 底部嵌入 ModelSelector 组件

交互逻辑:

  • 流式生成中禁止新建、切换、删除对话
  • 删除对话时 stopPropagation 防止触发选中
  • 删除当前活跃对话后,activeConversationId 置为 null

ModelSelector.tsx

模型选择下拉组件,嵌入侧边栏底部。

功能:

  • 点击展开/收起下拉列表
  • 点击外部区域自动关闭(mousedown 事件监听)
  • 当前选中模型高亮显示
  • chatStore.availableModels 读取模型列表

Empty.tsx

空状态占位组件,当前为简单文本展示,预留扩展。


5.4 状态管理

chatStore.ts

使用 Zustand 创建的全局状态 store,是前端的核心数据层。

状态定义:

| 字段 | 类型 | 初始值 | 说明 | |------|------|--------|------| | conversations | Conversation[] | [] | 对话列表 | | activeConversationId | string \| null | null | 当前活跃对话 ID | | selectedModel | string | '' | 当前选中的模型 ID | | availableModels | {id, name}[] | [] | 可用模型列表 | | isStreaming | boolean | false | 是否正在流式生成 | | sidebarOpen | boolean | true | 侧边栏是否展开 | | abortController | AbortController \| null | null | 用于中止流式请求 |

方法详解:

| 方法 | 说明 | 关键逻辑 | |------|------|----------| | loadConversations() | 从后端加载对话列表 | GET /api/conversations,仅加载元数据不含消息 | | loadModels() | 从后端加载可用模型 | GET /api/models,自动选中第一个模型 | | createConversation() | 创建新对话 | 本地生成 UUID,不调用后端,标题默认"新对话" | | selectConversation(id) | 切换到指定对话 | 若本地无消息则从后端加载 GET /api/conversations/:id | | deleteConversation(id) | 删除对话 | DELETE /api/conversations/:id,若删除的是当前对话则清空活跃 ID | | sendMessage(content) | 发送消息并处理流式响应 | 核心方法,详见下方 | | setModel(model) | 设置当前模型 | 直接更新 selectedModel | | toggleSidebar() | 切换侧边栏 | 取反 sidebarOpen | | stopStreaming() | 停止流式生成 | 调用 AbortController.abort() |

sendMessage 流式处理流程:

1. 若无活跃对话,先创建一个
2. 构造 userMessage 和 assistantMessage(空内容)
3. 将两条消息追加到当前对话,设置 isStreaming=true
4. 创建 AbortController
5. POST /api/chat 发送请求(含 messages, model, conversationId)
6. 读取 ReadableStream,逐行解析 SSE 数据:
   - type=conversation → 更新对话 ID(服务端可能分配新 ID)
   - type=content → 追加内容到 assistantMessage
7. 流结束后设置 isStreaming=false
8. 异常处理:AbortError 静默处理,其他错误显示"请求失败"

导出的类型:

interface Message {
  id: string
  role: 'user' | 'assistant' | 'system'
  content: string
  createdAt: number
}

interface Conversation {
  id: string
  title: string
  createdAt: number
  updatedAt: number
  messages: Message[]
}

5.5 Hooks

useTheme.ts

主题管理 Hook,支持亮/暗模式切换。

| 返回值 | 类型 | 说明 | |--------|------|------| | theme | 'light' \| 'dark' | 当前主题 | | toggleTheme | () => void | 切换主题 | | isDark | boolean | 是否暗色模式 |

逻辑:

  1. 初始化时优先读取 localStorage 中保存的主题
  2. 若无保存值,使用 prefers-color-scheme 媒体查询检测系统偏好
  3. 切换主题时同步更新 document.documentElement 的 class 和 localStorage

注意:当前应用 UI 以暗色为主设计,此 Hook 已实现但未在组件中实际调用。


5.6 工具函数

utils.ts

function cn(...inputs: ClassValue[]): string

合并 Tailwind CSS 类名的工具函数,组合 clsx(条件类名)+ twMerge(去重冲突类名)。在所有组件中广泛使用。


5.7 样式体系

index.css

全局样式文件,包含:

  • Google Fonts 导入(JetBrains Mono + Noto Sans SC)
  • Tailwind 指令(@tailwind base@tailwind components@tailwind utilities
  • CSS 变量与全局样式(暗色背景 #0a0a0f
  • 自定义工具类:
    • .scrollbar-thin — 细滚动条样式
    • .no-scrollbar — 隐藏滚动条
    • .glass — 毛玻璃效果
  • 流式光标动画(@keyframes blink + .streaming-cursor
  • 代码块样式覆盖(pre, code 字体与背景)

tailwind.config.js

TailwindCSS 配置,扩展了以下自定义设计 Token:

| Token | 值 | 说明 | |-------|------|------| | dark | #0a0a0f | 主背景色(暗夜黑) | | dark-100 | #1a1a2e | 次级背景色 | | dark-200 | #2a2a3e | 卡片/悬浮背景 | | dark-300 | #3a3a4e | 更浅的灰 | | accent | #00d4aa | 强调色(翠绿) | | accent-dark | #00b892 | 强调色暗色变体 | | fontFamily.mono | JetBrains Mono | 代码字体 | | fontFamily.sans | Noto Sans SC | 正文字体 |


6. 后端模块详解

6.1 应用入口与服务器

app.ts

Express 应用配置文件,定义中间件和路由挂载。

中间件链:

| 顺序 | 中间件 | 说明 | |------|--------|------| | 1 | cors() | 允许跨域请求 | | 2 | express.json({ limit: '10mb' }) | JSON 请求体解析 | | 3 | express.urlencoded({ extended: true, limit: '10mb' }) | URL 编码请求体解析 | | 4 | 路由中间件 | 挂载各 API 路由 | | 5 | 健康检查 | GET /api/health | | 6 | 错误处理 | 500 错误统一响应 | | 7 | 404 处理 | 未匹配路由统一响应 |

路由挂载:

| 前缀 | 路由模块 | 说明 | |------|----------|------| | /api/auth | authRoutes | 认证路由(占位) | | /api/chat | chatRoutes | AI 对话路由 | | /api/conversations | conversationRoutes | 对话管理路由 | | /api/models | modelRoutes | 模型列表路由 |

server.ts

本地开发服务器入口,监听 PORT 环境变量(默认 3001),处理 SIGTERM/SIGINT 优雅关闭。

index.ts

Vercel Serverless 部署入口,将 Express app 导出为 Vercel Serverless Function handler。


6.2 API 路由

chat.ts — AI 对话核心路由

端点: POST /api/chat

请求体:

interface ChatRequestBody {
  messages: Array<{ role: 'user' | 'assistant' | 'system'; content: string }>
  model?: string           // 可选,默认使用 OPENAI_MODEL 环境变量
  conversationId?: string  // 可选,若不存在则自动创建
}

响应格式: SSE (text/event-stream)

SSE 事件类型:

| type | 数据 | 说明 | |------|------|------| | conversation | { conversationId } | 返回对话 ID(新建或已有) | | content | { content } | AI 回复的文本片段 | | done | — | 流式生成完成 | | error | { error } | 错误信息 |

核心逻辑:

  1. 验证 messages 参数
  2. 确定使用的模型(请求参数 → 环境变量 → 默认 gpt-3.5-turbo
  3. 获取或创建对话,将用户消息存入 MemoryStore
  4. 设置 SSE 响应头
  5. 发送 conversation 事件(含对话 ID)
  6. 调用 OpenAI API 流式接口
  7. 逐 chunk 发送 content 事件
  8. 流结束后将完整 AI 回复存入 MemoryStore
  9. 发送 done 事件

OpenAI 客户端初始化:

function getOpenAIClient(): OpenAI  // 懒加载单例模式
  • apiKey:从 OPENAI_API_KEY 环境变量读取
  • baseURL:从 OPENAI_BASE_URL 环境变量读取(默认 https://api.openai.com/v1

conversations.ts — 对话管理路由

| 端点 | 方法 | 说明 | 响应 | |------|------|------|------| | /api/conversations | GET | 获取所有对话列表 | { success, data: Conversation[] } | | /api/conversations/:id | GET | 获取对话详情(含消息) | { success, data: ConversationDetail } | | /api/conversations/:id | DELETE | 删除对话 | { success } 或 404 |

models.ts — 模型列表路由

| 端点 | 方法 | 说明 | 响应 | |------|------|------|------| | /api/models | GET | 获取可用模型列表 | { success, data: [{id, name}] } |

模型列表来源:OPENAI_AVAILABLE_MODELS 环境变量(逗号分隔),默认 ['gpt-3.5-turbo', 'gpt-4', 'gpt-4o']

auth.ts — 认证路由(占位)

| 端点 | 方法 | 说明 | |------|------|------| | /api/auth/register | POST | 用户注册(TODO) | | /api/auth/login | POST | 用户登录(TODO) | | /api/auth/logout | POST | 用户登出(TODO) |

当前认证路由仅有骨架,未实现具体逻辑,所有 API 无需认证即可访问。


6.3 数据存储层

memoryStore.ts

基于内存的数据存储层,使用两个 Map 管理数据:

const conversations = new Map<string, Conversation>()  // 对话存储
const messages = new Map<string, Message[]>()           // 消息存储(按对话 ID 分组)

数据模型:

interface Conversation {
  id: string
  title: string
  createdAt: Date
  updatedAt: Date
}

interface Message {
  id: string
  conversationId: string
  role: 'user' | 'assistant' | 'system'
  content: string
  createdAt: Date
}

导出函数:

| 函数 | 签名 | 说明 | |------|------|------| | createConversation | (title: string) => Conversation | 创建对话,自动生成 ID | | getConversations | () => Conversation[] | 获取所有对话(按 updatedAt 降序) | | getConversation | (id: string) => Conversation \| undefined | 按 ID 获取单个对话 | | deleteConversation | (id: string) => boolean | 删除对话及其消息,返回是否成功 | | addMessage | (conversationId, role, content) => Message \| undefined | 向对话添加消息,更新 updatedAt | | getMessages | (conversationId: string) => Message[] | 获取对话的所有消息 |

ID 生成策略:

function generateId(): string {
  return Date.now().toString(36) + Math.random().toString(36).substring(2, 9)
}

基于时间戳(36 进制)+ 随机字符串,保证唯一性。

⚠️ 注意:数据存储在内存中,服务重启后所有数据丢失。生产环境需替换为持久化存储。


7. 数据模型

ER 关系图

Conversation 1 ──── * Message
     │                    │
     ├─ id (PK)           ├─ id (PK)
     ├─ title             ├─ conversationId (FK)
     ├─ createdAt         ├─ role ('user'|'assistant'|'system')
     └─ updatedAt         ├─ content
                          └─ createdAt

前后端模型差异

| 字段 | 前端 (chatStore) | 后端 (memoryStore) | |------|------------------|---------------------| | createdAt | number (时间戳) | Date (Date 对象) | | updatedAt | number (时间戳) | Date (Date 对象) | | Message.conversationId | 无 | string (FK) |

前端在接收后端数据时进行 new Date(c.createdAt).getTime() 转换。


8. API 接口文档

接口总览

| 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | POST | /api/chat | AI 对话(SSE 流式) | 无 | | GET | /api/conversations | 获取对话列表 | 无 | | GET | /api/conversations/:id | 获取对话详情 | 无 | | DELETE | /api/conversations/:id | 删除对话 | 无 | | GET | /api/models | 获取可用模型 | 无 | | GET | /api/health | 健康检查 | 无 | | POST | /api/auth/register | 注册(未实现) | 无 | | POST | /api/auth/login | 登录(未实现) | 无 | | POST | /api/auth/logout | 登出(未实现) | 无 |

统一响应格式

// 成功响应
{ success: true, data: T }

// 错误响应
{ success: false, error: string }

9. 核心流程

9.1 用户对话流程

用户打开应用
    │
    ├─ loadConversations() → GET /api/conversations
    ├─ loadModels() → GET /api/models
    │
    ▼
显示空状态页面(无活跃对话时)
    │
    点击"开始对话" → createConversation()(本地创建)
    │
    ▼
用户输入消息 → sendMessage(content)
    │
    ├─ 构造 userMessage + assistantMessage(空)
    ├─ POST /api/chat { messages, model, conversationId }
    │
    ▼
SSE 流式响应
    ├─ type=conversation → 同步对话 ID
    ├─ type=content → 逐字追加 AI 回复
    ├─ type=done → 流式结束
    │
    ▼
消息渲染(Markdown + 代码高亮)

9.2 对话切换流程

用户点击侧边栏对话项
    │
    ├─ 本地已有消息?→ 直接切换 activeConversationId
    ├─ 本地无消息?→ GET /api/conversations/:id 加载远程消息
    │
    ▼
渲染对话消息列表

9.3 流式中止流程

用户点击停止按钮
    │
    ├─ stopStreaming() → AbortController.abort()
    ├─ fetch 请求中断,抛出 AbortError
    ├─ catch 中静默处理 AbortError
    ├─ finally 中设置 isStreaming=false
    │
    ▼
保留已接收的部分回复

10. 部署方案

Vercel Serverless 部署

项目已配置 Vercel 部署,核心配置在 vercel.json

{
  "rewrites": [
    { "source": "/api/(.*)", "destination": "/api/index" },
    { "source": "/(.*)", "destination": "/index.html" }
  ]
}
  • /api/* 请求路由到 api/index.ts(Serverless Function 入口)
  • 其他请求路由到 index.html(SPA 前端)
  • 前端构建产物在 dist/ 目录

环境变量配置

| 变量名 | 必需 | 默认值 | 说明 | |--------|------|--------|------| | OPENAI_API_KEY | 是 | — | OpenAI API 密钥 | | OPENAI_BASE_URL | 否 | https://api.openai.com/v1 | API 基础 URL(可指向兼容后端) | | OPENAI_MODEL | 否 | gpt-3.5-turbo | 默认模型 | | OPENAI_AVAILABLE_MODELS | 否 | gpt-3.5-turbo,gpt-4,gpt-4o | 可选模型列表(逗号分隔) | | PORT | 否 | 3001 | 本地开发服务器端口 |


11. 项目运行方式

安装依赖

npm install

开发模式

同时启动前端 Vite 开发服务器和后端 Express 服务器:

npm run dev

等价于并行执行:

| 命令 | 说明 | |------|------| | npm run client:dev | 启动 Vite 开发服务器(默认 5173 端口) | | npm run server:dev | 启动 Nodemon 监听 api/ 目录变更,运行 tsx api/server.ts |

Vite 开发服务器配置了代理,将 /api 请求转发到 http://localhost:3001

单独启动

# 仅启动前端
npm run client:dev

# 仅启动后端
npm run server:dev

构建生产版本

npm run build

执行 tsc -b 类型检查 + vite build 打包,产物输出到 dist/ 目录。

其他命令

| 命令 | 说明 | |------|------| | npm run preview | 预览生产构建产物 | | npm run lint | 运行 ESLint 代码检查 | | npm run check | 运行 TypeScript 类型检查(tsc --noEmit) |

路径别名

项目配置了 @/*./src/* 的路径别名,在 tsconfig.jsonvite.config.ts(通过 vite-tsconfig-paths 插件)中同步生效。