一、项目初始化:打好开发地基
(一)目录结构设计:给代码安个「整齐的家」
一个清晰的目录结构是高效开发的开始,我们的 AI 单词拍照应用采用这样的分层设计:
-
src/:所有业务代码的「主战场」,包含组件、工具和页面逻辑 -
components/:组件专属目录,每个组件单独建文件夹(如PictureCard/包含index.jsx和style.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 类核心组件:
-
根组件(App) :全局状态管理者,负责协调 API 请求和整体布局
-
功能组件(PictureCard) :专注图片上传、预览和音频播放,是用户交互的核心
-
展示组件(如解释项) :纯展示型组件,只负责渲染数据(后续可扩展)
就像组装家具,每个零件都有明确用途,组合起来才稳固~
(二)组件通信:遵守「单向数据流」规则
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,让它返回对应的英文单词和解释。关键步骤:
-
构造提示词:明确告诉 AI 要返回什么格式(避免 AI「答非所问」):
const userPrompt = `分析图片内容,找出最能描述图片的一个英文单词(A1~A2级别)。
必须返回JSON:{
"image_discription": "图片描述",
"representative_word": "英文单词",
"example_sentence": "简单例句",
"explaination": "解释(以Look at...开头)",
"explaination_replys": ["回复示例"]
}`;
-
调用 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 } // 提示词
]
}]
})
});
-
处理 AI 返回结果:解析 JSON 并更新状态:
const data = await response.json();
const replyData = JSON.parse(data.choices[0].message.content);
setWord(replyData.representative_word); // 更新单词
setSentence(replyData.example_sentence); // 更新例句
这一步就像请了个「私人外教」,图片一上传就自动生成单词和解释
(下一篇将继续讲解音频生成、性能优化、难点攻克和最佳实践,敬请期待~)