前端工程化:React + TypeScript + Tailwind CSS 的组件化实践

摘要:在 AI 应用中,如何让复杂的训练数据以直观、美观的方式呈现给用户?本文基于一个真实的 AI 跑步教练项目,详细解析前端技术栈(React + TypeScript + Tailwind CSS)的工程化实践。我们将深入源码,展示如何利用 Server-Sent Events (SSE) 实现流式对话渲染、如何通过 React Hook Form 管理动态表单,以及如何集成 Recharts 绘制专业的运动图表。这套方案不仅提升了 UI 的开发效率,更让 AI 的“思考过程”变得可见、可交互。


一、背景:从“纯文本”到“交互式界面”

在项目初期,前端只是一个简单的聊天框,Agent 返回的所有内容都是纯文本。

痛点

  • 信息密度低:用户想看心率区间分布,却只能看到一大段文字描述。
  • 缺乏反馈:生成训练计划需要 20 秒,用户盯着空白屏幕不知道发生了什么。
  • 维护困难:随着功能增加,CSS 样式冲突频发,组件复用率极低。

为了解决这些问题,我重构了前端架构,引入了组件化设计流式渲染


二、核心实现:流式输出(Streaming)与 SSE

2.1 为什么选择 SSE?

相比 WebSocket,SSE(Server-Sent Events)是单向通信,非常适合“后端推、前端收”的 AI 对话场景,且原生支持断线重连。

文件位置:frontend/src/components/ChatBox.tsx

const handleSend = async (message: string) => {
  const response = await fetch('/api/v1/agent', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query: message })
  });

  const reader = response.body?.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    // 解析 SSE 格式的数据
    const chunk = decoder.decode(value);
    const lines = chunk.split('\n');
    
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = JSON.parse(line.slice(6));
        updateMessageBuffer(data.content); // 实时追加到 UI
      }
    }
  }
};

效果:用户能像看打字机一样看到 Agent 的回答逐字浮现,极大地缓解了等待焦虑。


三、核心实现:复杂表单的状态管理

3.1 动态训练计划录入

在“手动修正计划”功能中,用户需要动态增删每天的训练内容。我们使用 react-hook-form 配合 useFieldArray 实现。

文件位置:frontend/src/pages/PlanEditor.tsx

import { useForm, useFieldArray } from 'react-hook-form';

function PlanEditor() {
  const { control, register, handleSubmit } = useForm<PlanForm>();
  const { fields, append, remove } = useFieldArray({
    control,
    name: "daily_plans"
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {fields.map((field, index) => (
        <div key={field.id} className="flex gap-4 mb-2">
          <input {...register(`daily_plans.${index}.day`)} className="input-field" />
          <input {...register(`daily_plans.${index}.content`)} className="input-field" />
          <button type="button" onClick={() => remove(index)}>删除</button>
        </div>
      ))}
      <button type="button" onClick={() => append({ day: '', content: '' })}>添加一天</button>
    </form>
  );
}

优势

  • 性能优异:只重新渲染发生变化的字段,而非整个表单。
  • 类型安全:结合 TypeScript,确保表单数据结构与后端 Schema 完全一致。

四、核心实现:运动数据可视化

4.1 集成 Recharts 绘制心率图

文件位置:frontend/src/components/HeartRateChart.tsx

import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';

interface ChartProps {
  data: Array<{ time: string; hr: number; pace: number }>;
}

export const HeartRateChart: React.FC<ChartProps> = ({ data }) => {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <LineChart data={data}>
        <XAxis dataKey="time" stroke="#94a3b8" />
        <YAxis stroke="#94a3b8" />
        <Tooltip contentStyle={{ backgroundColor: '#1e293b', border: 'none' }} />
        <Line type="monotone" dataKey="hr" stroke="#ff6b6b" strokeWidth={2} name="心率" />
        <Line type="monotone" dataKey="pace" stroke="#4ecdc4" strokeWidth={2} name="配速" />
      </LineChart>
    </ResponsiveContainer>
  );
};

设计细节

  • 响应式布局:利用 ResponsiveContainer 确保图表在手机和桌面端都能完美适配。
  • Tailwind 配色:直接使用 Tailwind 的颜色代码,保持全站风格统一。

五、完整调用链追踪

5.1 从后端到前端的渲染流程

FastAPI Backend React Frontend 用户 FastAPI Backend React Frontend 用户 显示 Loading 骨架屏 loop [轮询任务状态] 点击“生成计划” POST /api/v1/tasks/submit 返回 task_id GET /api/v1/tasks/{task_id} {status: "running", progress: 50%} 更新进度条 {status: "success", result: {...}} 解析 JSON 并渲染 PlanTable 组件 显示精美的训练课表

六、踩坑记录与解决方案

坑1:Markdown 渲染 XSS 风险

现象:Agent 生成的回答包含 HTML 标签,直接渲染会导致页面错乱甚至安全风险。

解决方案

  • 使用 react-markdown 库进行安全渲染。
  • 配置 rehype-raw 插件时开启白名单过滤,只允许安全的标签(如 strong, em, table)。

坑2:Tailwind 打包体积过大

现象:生产环境 CSS 文件超过 1MB。

解决方案

  • tailwind.config.js 中严格配置 content 路径,确保 Tree-shaking 生效。
  • 启用 gzipbrotli 压缩,实际传输体积可降至 50KB 以下。

七、总结与展望

核心价值

  1. 体验升级:流式输出和动态图表让 AI 应用不再枯燥。
  2. 开发效率:组件化和 Hook 逻辑复用,让新功能开发速度提升 50%。
  3. 类型驱动:TypeScript 确保了前后端数据交互的稳定性,减少了线上 Bug。

后续优化

  1. 状态管理:引入 Zustand 或 Redux Toolkit 管理全局用户状态。
  2. PWA 支持:让 Web 应用具备离线访问和原生 App 般的安装体验。

八、完整源码

GitHub仓库AiRunCoachAgent

快速演示AiRunCoachAgent

核心文件清单

frontend/
├── src/
│   ├── components/
│   │   ├── ChatBox.tsx                  # 流式对话组件
│   │   └── HeartRateChart.tsx           # 运动图表组件
│   ├── pages/
│   │   ├── Coach.tsx                    # AI 教练主页
│   │   └── PlanEditor.tsx               # 计划编辑页
│   └── hooks/
│       └── useAgentStream.ts            # SSE 封装 Hook

如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发!有任何问题或建议,请在评论区留言讨论。 🏃‍♂️💨

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐