news 2026/8/19 7:09:33

基于React+Next.js+PostgreSQL的现代菜谱网站全栈开发实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于React+Next.js+PostgreSQL的现代菜谱网站全栈开发实践

1. 项目概述:从“菜谱网站”到“ReChef”的思考

最近几年,身边想学做饭、想吃得健康的朋友越来越多,但大家普遍有个痛点:网上菜谱要么是短视频一闪而过记不住细节,要么是图文教程里“适量”、“少许”让人摸不着头脑,更别提那些收藏了就等于做过了的“马了等于做了”系列。我自己也是个烹饪爱好者,在尝试复刻各种菜谱时,经常被不清晰的步骤、缺失的备料时间预估搞得手忙脚乱。所以,当我想动手搭建一个属于自己的菜谱网站时,目标就很明确了——它不能只是另一个菜谱列表,而应该是一个真正能帮人“成功复刻”的厨房助手。这就是“ReChef”的由来:“Re”意味着可复现(Reproducible)、可复盘(Review),而“Chef”则是我们每一个在厨房里探索的人。

ReChef Recipe Website 的核心定位,是一个注重结构化数据、可操作性指引和社区互动验证的现代菜谱平台。它区别于传统博客式菜谱的关键在于,它将每一道菜分解为精确、可量化的组成部分:从食材的精确克数、毫升数,到步骤的详细分解、所需工具,再到精确到分钟的准备与烹饪时间。其潜在用户非常广泛,包括烹饪新手、追求效率的上班族、希望记录家庭味道的美食爱好者,以及想要标准化操作流程的私厨或小型餐饮从业者。这个项目看似是内容展示,实则涉及前端用户体验、后端数据建模、内容生产工具链乃至社区运营机制的全栈实践,是一个能充分锻炼和展示综合开发能力的绝佳练手项目。

2. 核心设计思路与技术选型

一个菜谱网站,技术栈的选择直接决定了开发效率、未来可扩展性和用户体验。经过权衡,我为本项目设计了一套以现代Web技术为核心、兼顾开发敏捷与性能的方案。

2.1 前端架构:React + Next.js 的组合拳

前端我选择了React作为UI库,并搭配Next.js框架。这几乎是目前构建高性能、SEO友好型内容网站的首选方案之一。选择React是因为其组件化开发模式与菜谱网站的结构高度契合。一个菜谱页面可以拆分为:标题头图组件、食材清单组件、分步教程组件、厨具提示组件、用户评论组件等。每个组件独立开发、维护和复用,极大提升了开发效率。

Next.js 的引入则解决了几个关键问题:一是服务端渲染(SSR)和静态生成(SSG)。对于菜谱详情页这种内容相对固定、但访问量可能很大的页面,使用SSG在构建时生成HTML,能获得极致的加载速度和搜索引擎优化效果。二是基于文件系统的路由,让页面管理变得非常直观,比如pages/recipes/[id].js就对应了菜谱详情页。三是内置的API路由功能,方便我们快速构建后端接口,在项目初期可以前后端一体开发,快速验证想法。

在UI组件库上,我没有选择庞大的Ant Design或MUI,而是采用了Tailwind CSS这种实用优先的CSS框架。对于需要高度定制化设计的内容型网站,Tailwind 提供了无与伦比的灵活性。我可以快速构建出独特的设计语言,比如为“烹饪步骤”卡片设计特定的阴影和交互效果,而无需与预设组件库的样式做斗争。

2.2 后端与数据库:Prisma + PostgreSQL 的强类型搭档

后端的核心是数据模型。一个菜谱包含哪些信息?这需要精心设计。我使用Prisma作为ORM(对象关系映射)工具,搭配PostgreSQL数据库。Prisma 的Schema定义语言非常直观,能清晰地描绘出数据之间的关系。

model Recipe { id String @id @default(cuid()) title String description String? prepTime Int // 准备时间,单位:分钟 cookTime Int // 烹饪时间,单位:分钟 servings Int // 份量 difficulty Difficulty // 枚举:EASY, MEDIUM, HARD author User @relation(fields: [authorId], references: [id]) authorId String ingredients Ingredient[] steps Step[] tools Tool[] categories Category[] images Image[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Ingredient { id String @id @default(cuid()) name String quantity Float unit String // 克、毫升、个、汤匙等 recipe Recipe @relation(fields: [recipeId], references: [id]) recipeId String }

选择PostgreSQL是因为其强大的JSON支持、全文搜索以及可靠性。未来如果我们需要实现“根据家里现有食材推荐菜谱”的复杂查询,PostgreSQL的能力会非常有用。Prisma则保证了从数据库到后端TypeScript代码的完全类型安全,大大减少了运行时错误。

注意:在数据模型设计时,切忌过度标准化。例如,最初我曾将“单位(unit)”单独建表,但发现这会导致数据录入和查询变得复杂。对于菜谱这种场景,将unit作为Ingredient模型中的一个字符串字段是更务实的选择,虽然可能存在“克”和“g”这样的同义词,但可以在数据清洗或前端展示层统一处理。

2.3 内容存储与图像处理:Cloudinary + 文本编辑器集成

菜谱内容除了结构化数据,还有富文本描述和大量图片。对于步骤描述,我选择了TipTap编辑器。它是一个无头(headless)的编辑器框架,可以完全自定义其外观和功能。我为其定制了专门的“步骤”块,允许用户上传步骤图,并和文字说明绑定。编辑器输出的内容是JSON格式,便于前端灵活渲染。

图片处理是内容网站的性能关键。我直接使用了CloudinaryImgix这样的专业图像CDN服务。它们的核心价值在于:用户上传原始高分辨率图片后,服务端可以按需生成不同尺寸、不同格式(如WebP)、并应用优化压缩的图片。前端只需根据设备屏幕大小,请求对应尺寸的图片URL即可。这省去了自己搭建图片处理服务器的巨大运维成本,并确保了全球范围内的快速加载。

3. 核心功能实现与细节解析

有了技术栈,接下来就是实现核心功能。ReChef的核心体验围绕“发布菜谱”和“浏览复现菜谱”两个环节展开。

3.1 菜谱创建流程:结构化数据的录入体验

创建一个易用且强大的菜谱发布表单是挑战。前端需要构建一个动态表单,允许用户:

  1. 填写基础信息(标题、描述、时间、份量)。
  2. 动态增删食材行(每行包括食材名、数量、单位)。
  3. 动态增删步骤(每步包括文字描述和可选的步骤图)。
  4. 选择或输入所需的厨具、关联的菜系分类。

这里的关键是状态管理。我使用React的useStateuseReducer来管理这个复杂的表单状态。对于食材和步骤列表,每个列表项都是一个对象,增删改操作都需要不可变地更新状态,以确保UI正确响应。

// 简化示例:管理食材列表的状态 const [ingredients, setIngredients] = useState([{ id: 1, name: '', quantity: '', unit: '克' }]); const addIngredient = () => { setIngredients([...ingredients, { id: Date.now(), name: '', quantity: '', unit: '克' }]); }; const updateIngredient = (id, field, value) => { setIngredients(ingredients.map(item => item.id === id ? { ...item, [field]: value } : item )); };

表单提交时,前端将状态整理成符合后端API预期的JSON格式,通过Next.js的API路由发送到服务器。服务器端(在API路由中)使用Prisma Client将数据写入PostgreSQL。这里务必做好数据验证,我使用zod库在前后端定义一致的验证模式,确保数据的完整性和准确性。

3.2 菜谱详情页:可复现性的视觉呈现

详情页是价值的最终体现。其设计原则是:信息层次清晰,关键数据一眼可见。

  • 顶部英雄区:展示主图、标题、描述、作者、时间戳。突出显示“总耗时”、“难度”和“份量”,用图标和醒目数字呈现。
  • 食材区:采用两栏布局(桌面端),一栏是食材清单,可勾选(前端用localStorage实现,模拟备料过程);另一栏是营养估算(如果未来接入数据)或厨具清单。
  • 步骤区:这是核心。每个步骤卡片包含步骤序号、详细说明、相关图片(点击可放大)。我采用了“渐进式图片加载”,先显示一个极小的模糊图块,再加载清晰图,提升感知速度。一个细节是,在移动设备上,当前正在查看的步骤会高亮并始终保持在视图舒适位置。
  • 互动区:包含“收藏”、“做过”按钮。“做过”功能是ReChef的特色。用户点击后,可以上传自己的成品图,并选择“完全成功”、“略有调整”、“失败”等状态,还可以记录自己的心得笔记。这些用户生成内容(UGC)是社区信任的基石。

3.3 搜索与发现:让菜谱被找到

一个堆满菜谱但找不到想要内容的网站是失败的。我实现了多维度发现机制:

  1. 全文搜索:利用PostgreSQL的全文搜索功能,对菜谱标题、描述、食材名进行索引。搜索“番茄鸡蛋”,能同时匹配到标题和含有这些食材的菜谱。
  2. 分类与标签过滤:如菜系(川菜、烘焙)、场合(早餐、宴客)、主食类型等。
  3. 智能筛选:这是亮点。用户可以通过滑块筛选“总时长”(如30分钟以内)、“难度”。更实用的是“食材筛选”:用户输入“鸡胸肉、西兰花”,系统能找出主要用这两种食材的菜谱。这背后是相对复杂的数据库查询,需要关联RecipeIngredient表,并进行分组和计数。

4. 部署、优化与未来扩展思考

4.1 部署与性能优化

项目开发完成后,我选择部署在Vercel上。Vercel 对Next.js应用是“零配置”部署,自动配置CDN、SSL,并且与GitHub集成,实现自动化部署。对于数据库,我使用了托管式的PostgreSQL服务(如Supabase或Neon),免去了数据库运维的麻烦。

性能优化方面,除了前述的图片优化和SSG,我还做了以下工作:

  • 字体优化:使用next/font自动托管和优化Google Fonts,消除布局偏移。
  • 代码分割:Next.js自动进行代码分割。我确保大型的第三方库(如某个复杂的图表库)被动态导入,不阻塞首屏。
  • API响应缓存:对于不常变动的数据,如菜谱分类列表,在API路由中设置Cache-Control头,让Vercel的边缘网络进行缓存,大幅减少数据库查询和响应时间。

4.2 实测中遇到的坑与解决方案

  1. 富文本编辑器内容回显问题:TipTap编辑器输出的JSON数据,在前端渲染时需要用到对应的渲染组件。最初我直接使用dangerouslySetInnerHTML的方式来回显HTML(由后端转换),这既不安全也不灵活。解决方案是统一使用TipTap提供的generateHTML函数在客户端渲染,或者使用服务端渲染,但必须确保前后端的TipTap扩展配置完全一致,否则样式会错乱。

  2. 图片上传状态管理:在创建菜谱时,步骤图的上传是异步的。如果用户在上传完成前就提交表单,会导致数据不一致。解决方案是,在前端先将图片上传到Cloudinary并获得URL,再将这个URL作为步骤数据的一部分提交给后端。在上传过程中,禁用提交按钮并给出加载提示。

  3. 数据库连接池耗尽:在Vercel的Serverless环境下,每个API请求都可能创建一个新的数据库连接,如果请求量大,容易耗尽连接池。解决方案是,使用Prisma时,务必在Next.js的API路由中创建一个全局共享的Prisma Client实例,并注意在开发环境下避免因热重载创建过多实例。

4.3 未来可扩展的方向

ReChef的基础框架已经搭建完成,但还有很大的想象空间:

  • AI辅助应用:接入大语言模型API,实现“清空冰箱”功能:用户输入现有食材,AI推荐可制作的菜谱。或者根据菜谱步骤,自动生成详细的采购清单。
  • 视频集成:允许用户为关键步骤上传短视频(如“颠勺”技巧),与图文步骤互补。
  • 社交与清单功能:强化“做过”的社交属性,形成feed流。开发“本周食谱计划”功能,自动生成购物清单。
  • 数据开放:提供结构化的API,允许美食博主同步发布内容,或让智能厨电设备接入标准化的菜谱指令。

搭建ReChef的过程,远不止是做一个网站,而是对“如何将模糊的经验转化为可执行的结构化知识”的一次深度实践。它让我深刻体会到,好的工具应该隐形,它不增加使用者的负担,而是通过精心的设计,让复杂的事情变得条理清晰、易于上手。当你看到用户留言说“按照这个方子第一次做糖醋排骨就成功了”,那种成就感,远超代码本身带来的快乐。技术最终要服务于人,服务于生活里那些热腾腾的烟火气。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/19 7:08:49

技术人做产品,升级前别漏掉用户迁移成本

技术人做产品,升级前别漏掉用户迁移成本 技术背景能帮助 PM 判断实现边界、和研发讨论风险,但不能代替用户迁移的判断。版本升级里,架构是否整洁只是一个维度;接口兼容、数据状态和用户原有操作能否延续,往往更早影响交…

作者头像 李华
网站建设 2026/8/19 7:08:45

自制四象限功率计:原理、设计与应用全解析

1. 项目概述:一个简单四象限功率计能做什么?如果你玩过电子负载、测试过电源,或者捣鼓过电机驱动、能量回收电路,那你肯定对“功率流向”这个概念不陌生。简单说,就是电到底是从设备流出去,还是流进来。传统…

作者头像 李华
网站建设 2026/8/19 7:06:48

多专家协作低效根源与破解:从系统思维到接口契约的工程实践

1. 项目概述:当“专家”太多,协作反而变慢了最近在复盘几个大型跨团队项目时,我反复琢磨一个现象:明明每个环节的负责人都是各自领域的顶尖专家,技术方案单独拿出来看都堪称完美,但项目整体推进起来却异常滞…

作者头像 李华
网站建设 2026/8/19 7:05:38

VBA全局变量持久化:参数表与注册表实战方案解析

如果你在Excel VBA项目中遇到过这样的困境:一个宏在A电脑上运行正常,换到B电脑就报错;或者修改了一个全局配置后,需要手动通知所有用户更新代码;又或者,你辛苦编写的VBA工具,因为配置信息散落在…

作者头像 李华