2026 年家庭菜谱记录工具怎么选:从功能边界和小程序> CSDN 版本说明:本文不做使用引导,也不把重点放在产品推荐上,而是从家庭菜谱记录这个场景出发,拆一下不同工具背后的功能边界、数据结构和成本来源。
1. 家庭菜谱记录和公开菜谱平台不是同一个问题
很多人说"菜谱工具",其实会混在一起说三类东西。
第一类是公开菜谱平台,例如下厨房、豆果美食、美食杰、香哈菜谱。它们的核心价值是内容量,适合解决"这道菜别人怎么做"的问题。
第二类是通用记录工具,例如备忘录、微信收藏、表格。它们没有菜谱业务约束,记录自由,但长期维护成本会随菜品数量上升。
第三类是家庭菜谱管理工具。它解决的不是"世界上有多少菜谱",而是"我家有哪些菜、谁爱吃、今天从哪几道里面选、能不能让家人提前点菜"。
从工程角度看,这三类工具的数据模型完全不同。
公开菜谱平台更像内容社区,重点是内容分发、搜索、收藏和评论。通用记录工具更像无 schema 的笔记。家庭菜谱管理工具则要把菜品、分类、用料、做法、图片、点单、导出这些对象组织起来。
所以免费够不够用,不能只看"能不能看到菜谱"。要看它是否支持自己的长期数据沉淀。
2. 示例小程序的页面结构
从 miniprogram/app.json 可以看到,这个小程序不是单页 Demo,而是围绕"菜谱记录 + 抽菜 + 点单 + 日记 + 个人中心"拆成多个页面。
json
{
"pages": [
"pages/index/index",
"pages/my-orders/index",
"pages/order-detail/index",
"pages/add-menu/index",
"pages/menu-detail/index",
"pages/menu-cooking/index",
"pages/draw/index",
"pages/menu-poster/index",
"pages/food-diary/cup/index",
"pages/food-diary/meal/index"
],
"window": {
"navigationBarTitleText": "今天吃什么家庭菜谱管理"
}
}
这里有几个关键入口。
pages/index/index 负责首页菜品列表、分类筛选、搜索、今日点单提醒。
pages/add-menu/index 负责新增和编辑菜品。
pages/menu-detail/index 和 pages/menu-cooking/index 负责菜品详情和烹饪模式。
pages/draw/index 负责从自己的菜品库里随机抽菜。
pages/menu-poster/index 负责生成家庭、双人、个人菜单图。
pages/my-orders/index、pages/order-detail/index、pages/share/index 这一组负责邀请点单。
从这个结构就能看出来,它的核心不是公开菜谱内容,而是围绕用户自己的菜单资产做操作。
3. 菜品为什么要结构化
如果只是写一条备忘录,字段可以很随意。
但家庭菜谱一旦超过十几道,后面就会遇到分类、搜索、复盘、导出的问题。字段不稳定,后面的功能都会变难。
示例项目里,菜品新增页不只记录菜名,还记录分类、备注、图片、用料和做法。用料和步骤还有数量和长度限制,相关工具函数在 miniprogram/utils/menuRecipe.js。
js
const MENU_RECIPE_LIMITS = {
maxIngredients: 12,
maxSteps: 10,
ingredientNameMaxLength: 20,
ingredientAmountMaxLength: 15,
stepDescMaxLength: 120,
};
这个限制看起来普通,但很实用。
家庭菜谱不是专业出版菜谱,不需要无限长的步骤。限制字段长度,可以减少脏数据,也能避免详情页和导出图被极端内容撑坏。
cloudfunctions/dataManager/index.js 里也做了服务端校验,前端和云函数保持同一套限制。
js
function validateAndNormalizeMenuPayload(data = {}, existingData = null) {
const merged = existingData ? { ...existingData, ...data } : data;
const rawIngredients = parseArrayLike(merged.ingredients);
const rawSteps = parseArrayLike(merged.steps);
if (rawIngredients.length > MENU_RECIPE_LIMITS.maxIngredients) {
throw new Error(`用料最多填写${MENU_RECIPE_LIMITS.maxIngredients}项`);
}
if (rawSteps.length > MENU_RECIPE_LIMITS.maxSteps) {
throw new Error(`做法最多填写${MENU_RECIPE_LIMITS.maxSteps}步`);
}
}
这类校验适合放在服务端再做一遍。小程序前端可以被绕过,真正的数据边界还是要落在云函数里。
4. 免费限制通常限制的是资源,不应该限制核心数据资产
很多菜谱工具的付费边界会落在"菜谱数量"上。对家庭菜谱记录来说,这个边界比较敏感。
因为用户真正想积累的是自己的菜品库。如果 20 道或 50 道以后就不能继续记,工具很快会变成试用,而不是长期档案。
示例项目选择了另一种方式,在 cloudfunctions/dataManager/index.js 里限制每日新增速度,而不是限制长期菜品总量。
js
const LIMIT_CONFIG = {
menus: {
dailyAddLimit: 35,
desc: '今日新增菜品次数已达上限(35次),请明天再试'
},
menuImageUpdate: {
dailyUpdateLimit: 10,
desc: '今日修改菜品图片次数已达上限(10次),请明天再试'
}
};
后面的统计也考虑了北京时间自然日。
js
function getBeijingDayKey(baseDate = new Date()) {
const UTC_MS = baseDate.getTime();
const CST_MS = UTC_MS + 8 * 60 * 60 * 1000;
const d = new Date(CST_MS);
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${y}-${m}-${day}`;
}
这种设计比较适合家庭场景。
新增菜品、替换图片、生成图片这些操作会消耗云存储和计算资源,所以限制每日频率合理。但长期菜品总量是用户自己的核心数据,如果直接卡总量,就会影响长期使用。
5. 数据隔离是小程序里不能省的部分
家庭菜谱属于私人数据。哪怕只是菜名和备注,也可能包含家人偏好、饮食习惯、聚餐记录。
示例项目把通用 CRUD 收敛到 cloudfunctions/dataManager/index.js,所有 list 查询都会自动加当前用户的 _openid 条件。
js
case 'list':
const userWhere = { ...where, _openid: userOpenId };
return await listData(collection, userWhere, orderBy, limit, skip);
更新和删除时,也会先读文档并确认归属。
js
if (!existingData || existingData._openid !== userOpenId) {
throw new Error('数据不存在或无权限修改');
}
这比在每个页面里手写权限判断更稳。前端只调用 miniprogram/utils/cloudApi.js 的 add、update、delete、get、list,权限逻辑集中在云函数里。
6. 随机选菜不是随机公开菜谱,而是随机自己的菜单
"今天吃什么"这个功能如果只是随机一个网上菜名,实用性有限。家庭场景更需要从自己的菜单里抽,因为这些菜默认满足三个条件。
自己会做。
家里人能接受。
食材和做法相对熟悉。
pages/draw/index.js 里会读取当前用户的 menus 集合,再按用户选择的数量抽取。
js
const res = await CloudAPI.list('menus', {
orderBy: { field: 'createTime', order: 'desc' },
limit: 1000
});
这说明它的随机逻辑依赖私有菜品库,而不是公开内容库。这个差异决定了产品形态。
公开菜谱平台适合发现陌生菜,家庭菜单工具适合在已有偏好里做选择。
7. 邀请点单的服务端模型
家庭或朋友聚餐时,点菜不是一个人的动作。示例项目用 orderService 单独处理点单相关逻辑。
cloudfunctions/orderService/index.js 支持这些操作。
js
// listCategoriesByOwner
// listMenusByOwner
// createOrder
// listInviterToday
// getOrder
// createGroup
// getGroupDetail
// finishGroup
创建点单时,核心字段包括邀请人、被邀请人、菜品明细、多人单 ID、用餐时段、状态和日期索引。
js
const doc = {
inviterOpenId,
inviteeOpenId,
groupId: groupId || null,
mealPeriod: mealPeriod || null,
items: items.map(x => ({
dishId: x.dishId,
dishName: x.dishName,
image: x.image || '',
price: Number(x.price || 0),
count: Number(x.count || 1)
})),
totalPrice,
status: 'created',
createdAt: new Date(),
dateKey: todayKey()
};
多人单还做了 24 小时过期校验。这个设计避免旧链接长期可写,也减少后续数据整理成本。
从实现上看,邀请点单不是简单分享页面,而是一套轻量订单模型。哪怕不涉及真实支付,也需要状态、归属、过期和查询。
8. 菜单导出为什么容易成为付费或额度功能
菜单导出不是简单截图。示例项目在 pages/menu-poster/index.js 里做了家庭、双人、个人三种风格,还支持长图和分页。
js
const DIRECT_EXPORT_MAX = 18;
const EXPORT_PAGE_SIZE = 12;
const DAILY_FREE_EXPORT_LIMIT = 2;
菜单样式也单独建模。
js
const MENU_STYLES = {
family: { label: '家庭', exportTitle: '家庭私享菜单' },
couple: { label: '双人', exportTitle: '双人小馆菜单' },
personal: { label: '个人', exportTitle: '我的常做菜' }
};
这类功能的成本主要在 Canvas 渲染、图片临时链接、图片资源读取和保存。对用户来说只是点一下导出,对系统来说会牵涉图片资源和渲染链路。
所以比较合理的免费策略是,核心记录长期可用,图片处理和导出设置每日额度。
9. 几类工具的工程侧对比
| 类型 | 主要目标 | 数据模型 | 优点 | 局限 |
|---|---|---|---|---|
| 公开菜谱平台 | 查做法、找灵感 | 公开菜谱、收藏、评论、课程 | 内容多,适合学新菜 | 不适合沉淀自家版本 |
| 通用记录工具 | 自由记录 | 文本、图片、表格 | 零学习成本,完全自由 | 分类、搜索、导出、协作都要自己搭 |
| 家庭菜谱小程序 | 记录自家菜单 | 菜品、分类、用料、步骤、点单、导出 | 适合长期维护家庭口味 | 不适合替代大型公开菜谱社区 |
如果目标是学一道没做过的菜,公开菜谱平台更合适。
如果目标是把家里的常吃菜整理成长期档案,结构化菜谱工具更合适。
如果只是偶尔记录三五道菜,备忘录和表格也够。
10. 对开发者的几个启发
做家庭菜谱工具,不要一开始就拼内容量。公开菜谱平台已经有足够多内容,同类小程序更适合从"私人数据管理"切入。
核心功能优先级可以这样排。
第一,菜品结构化录入,包括分类、图片、备注、用料、步骤。
第二,快速找回,包括分类、搜索、详情和烹饪模式。
第三,低成本决策,包括随机抽菜和按数量抽菜。
第四,多人协作,包括邀请点单、多人单、今日点单提醒。
第五,可分享输出,包括菜单长图和分页导出。
免费策略也要贴近成本结构。文本记录和基础查询成本低,应该尽量稳定开放。图片处理、云端存储、Canvas 导出、AI 或第三方服务调用成本高,可以设置每日额度。
这样既能控制资源,又不会破坏用户最核心的数据积累。
11. 小结
家庭菜谱记录工具的关键,不是谁的公开菜谱最多,而是谁更适合沉淀自己的菜单。
从代码实现看,一个实用的家庭菜谱小程序至少要处理四件事:私有菜品库、每日额度、点单协作、菜单导出。
免费和付费的差异也不只是价格问题,本质是资源和数据边界问题。限制图片处理和导出次数,比限制长期菜品总数更适合家庭菜谱这个场景。
如果后续做同类项目,可以先把菜品数据模型、用户数据隔离、每日额度和菜单导出链路设计清楚,再考虑页面包装和增长功能。代码实现看免费与付费差异