从 0 到 1 开发 React AI 单词拍照应用(上):基础搭建与核心功能实现

78 阅读4分钟

一、项目初始化:打好开发地基

(一)目录结构设计:给代码安个「整齐的家」

一个清晰的目录结构是高效开发的开始,我们的 AI 单词拍照应用采用这样的分层设计:

  • src/:所有业务代码的「主战场」,包含组件、工具和页面逻辑

  • components/:组件专属目录,每个组件单独建文件夹(如PictureCard/包含index.jsxstyle.css),实现「组件即模块」

  • public/:存放 HTML 模板、图标等静态资源,这些文件不会被 Webpack 处理

  • libs/:封装通用工具函数(如音频处理、API 请求),避免代码重复

  • .env.local:安全存储 API 密钥(如VITE_KIMI_API_KEY),千万别把它提交到代码仓库哦~

这种结构就像给代码分了「房间」,找文件时再也不用翻箱倒柜啦~

(二)技术栈选型:合适的工具才顺手

开发 React 应用就像做菜,选对「食材」很重要:

  • 核心框架:React 18+(函数组件 + Hook 模式,代码更简洁)
  • 状态管理:useState(小项目足够用,复杂场景再考虑 Redux/Zustand)
  • 网络请求:原生fetch(轻量无依赖,避免为了简单需求引入 Axios)
  • 样式方案:CSS Modules(组件样式隔离,再也不怕类名冲突)
  • 环境变量:import.meta.env(现代浏览器原生支持,比传统dotenv更清爽)

二、组件化开发:像搭积木一样拼 UI

(一)组件划分:每个组件只干一件事

遵循「单一职责原则」,我们把应用拆成 3 类核心组件:

  1. 根组件(App) :全局状态管理者,负责协调 API 请求和整体布局

  2. 功能组件(PictureCard) :专注图片上传、预览和音频播放,是用户交互的核心

  3. 展示组件(如解释项) :纯展示型组件,只负责渲染数据(后续可扩展)

就像组装家具,每个零件都有明确用途,组合起来才稳固~

(二)组件通信:遵守「单向数据流」规则

React 组件通信就像「上下级汇报工作」,有严格的规则:

  • 父传子:通过props传递数据(如 App 把word传给 PictureCard)

  • 子传父:通过回调函数传递操作(如 PictureCard 用uploadImg回调告诉 App「图片上传好了」)

  • 状态归属

    • 公共状态(如图片分析结果)放父组件

    • 私有状态(如组件展开 / 折叠)放子组件

    • 子组件想改状态?必须「请示」父组件,不能自己做主~

这样的规则能避免状态混乱,就像交通规则保证道路通畅一样~

三、核心功能实现(上):从图片上传到 AI 分析

(一)图片上传与预览:把图片「搬进」应用

用户上传图片的流程,本质是把本地文件转成浏览器能处理的格式:

// PictureCard组件中的上传逻辑
const uploadImgData = (e) => {
  const file = e.target.files?.[0]; // 获取用户选择的文件
  if (!file) return; // 没选文件就退出
  
  // 用FileReader把文件转成Base64格式
  const reader = new FileReader();
  reader.readAsDataURL(file); 
  reader.onload = () => {
    const base64Data = reader.result; // 得到Base64字符串
    setImgPreview(base64Data); // 更新预览图
    uploadImg(base64Data); // 通知父组件处理图片
  };
};

✨ 小细节:通过accept=".jpg,.jpeg,.png,.gif"限制文件类型,避免用户传错格式~

(二)调用 AI 大模型:让图片「说」出英文单词

核心逻辑是把图片传给 AI,让它返回对应的英文单词和解释。关键步骤:

  1. 构造提示词:明确告诉 AI 要返回什么格式(避免 AI「答非所问」):

const userPrompt = `分析图片内容,找出最能描述图片的一个英文单词(A1~A2级别)。
必须返回JSON:{ 
  "image_discription": "图片描述", 
  "representative_word": "英文单词", 
  "example_sentence": "简单例句", 
  "explaination": "解释(以Look at...开头)", 
  "explaination_replys": ["回复示例"]
}`;
  1. 调用 AI 接口:用fetch发送请求,记得带上认证信息:

// App组件中的AI调用逻辑
const response = await fetch('https://api.moonshot.cn/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    // 从环境变量拿密钥,安全又方便
    'Authorization': `Bearer ${import.meta.env.VITE_KIMI_API_KEY}`
  },
  body: JSON.stringify({
    model: 'moonshot-v1-8k-vision-preview', // 指定大模型
    messages: [{
      role: 'user',
      content: [
        { type: "image_url", image_url: { url: imageData } }, // 图片数据
        { type: "text", text: userPrompt } // 提示词
      ]
    }]
  })
});
  1. 处理 AI 返回结果:解析 JSON 并更新状态:

const data = await response.json();
const replyData = JSON.parse(data.choices[0].message.content);
setWord(replyData.representative_word); // 更新单词
setSentence(replyData.example_sentence); // 更新例句

这一步就像请了个「私人外教」,图片一上传就自动生成单词和解释

(下一篇将继续讲解音频生成、性能优化、难点攻克和最佳实践,敬请期待~)