家里每天最头疼的问题是”今天吃什么”。做了个家庭餐单小程序来治这个病:记录日常菜品,智能生成一周餐单,一键分享给家人。项目还在迭代中,这篇记录目前开发过程中觉得值得说的几个点。
产品思路
痛点很具体:做饭的人每天都在决策,决策成本其实很高。小程序要做的不是”菜谱大全”,而是”决策工具”——把”吃什么”从每天想 20 分钟,变成 10 秒内出结果。
核心功能三条:
- 菜品库:家人各自录入常做的菜,按分类整理
- 智能排餐:根据菜品库自动生成每日/每周餐单,均衡搭配、尽量不重复
- 分享协作:餐单一键分享到家庭群,家人可见、可点赞可吐槽
技术选型:uni-app + 微信云开发
选 uni-app 是为了保留跨端可能(以后可能上支付宝/抖音小程序),UI 层用微信原生组件风格开发,适配 rpx 基准。
后端直接用微信云开发:云数据库存储菜品和餐单,云函数处理业务逻辑,免运维。对小团队和个人项目来说,这是性价比最高的方案——不用管服务器,按量付费,量小几乎免费。
组件化:把 UI 变成积木
小程序开发最容易变成”一个页面 500 行 WXML”。这次我强制自己做了组件拆分:
dish-card:菜品卡片,展示图片、名称、标签、操作按钮meal-plan:一周餐单视图,支持横向滑动切换category-tabs:分类筛选栏share-card:分享海报卡片
组件用 properties 接收数据、triggerEvent 抛事件,父子通信规范。好处是页面代码大幅瘦身,改样式只动组件不动页面。餐单生成算法放在独立的工具模块里,和 UI 完全解耦:
// 简单版:按分类均衡取菜,尽量不连续重复function generatePlan(dishPool, days, slotsPerDay) { const plan = []; const lastUsed = new Map(); // dish -> 上次出现的下标 for (let d = 0; d < days; d++) { const dayPlan = []; for (let s = 0; s < slotsPerDay; s++) { const candidates = dishPool.filter( (dish) => !dayPlan.includes(dish) && (lastUsed.get(dish.id) ?? -9) < d - 2 ); const pick = candidates[Math.floor(Math.random() * candidates.length)] || dishPool[Math.floor(Math.random() * dishPool.length)]; lastUsed.set(pick.id, d); dayPlan.push(pick); } plan.push(dayPlan); } return plan;}算法很朴素,核心约束就两条:同一天不重复、同一道菜间隔至少两天。够用,且家人能看懂逻辑,好提需求。
踩过的坑
云函数超时。 第一版把餐单生成逻辑放云函数里,菜品库大了之后偶尔超时。后来把生成逻辑挪到前端本地执行(菜品数据本来就在本地缓存),云函数只做数据读写,问题消失。
分享卡片。 微信的 onShareAppMessage 只能自定义标题和图片路径,不能直接分享带参数的富文本卡片。方案是做了一张 Canvas 海报图:wx.createCanvasContext 画菜品图 + 文字 + 二维码,分享出去既有信息量又能引流回小程序。
rpx 的适配陷阱。 750 基准下,1rpx = 屏幕宽 / 750,听起来简单,但给 Canvas 画海报时用的是 px,两边换算忘了一除,海报文字直接飞出屏幕。教训:混用 rpx 和 px 的场景,换算要在同一个函数里做,别散落在各处。
下一步
- 菜品库接入家人协作编辑,权限简化成”家庭成员均可增删”
- 按营养标签做更聪明的均衡搭配
- 上线后根据真实使用数据调排餐逻辑
做这个小程序最大的体会:工具类产品的核心是”决策效率”,功能可以简单,但决策链路必须短。这个思路在写企业系统时同样适用。