
简介这套菜谱微信小程序源码基于云开发模式构建适合美食类创业者、小程序开发初学者以及需要快速落地菜谱应用的开发者。无需自备域名和服务器导入微信开发者工具即可运行并提交审核大幅降低搭建门槛。压缩包共包含182个文件以100个PNG图片、23个JS逻辑文件、20个JSON配置、18个WXSS样式和17个WXML结构为主另有4个JPG封面图整体体积仅1.79MB结构紧凑、便于整理与二次开发。目前已有991人学习下载适合用于学习云开发接口调用、小程序页面布局以及数据分类展示思路。源码仿照京细菜谱的内容组织方式覆盖八大菜系、特色食品、特殊场合、热门功效、人群细分、烘焙甜品、口味和食材等详细分类同时提供用户页、列表页、详情页等完整交互流程开发者可直接沿用其设计语言和模块划分快速生成专属菜谱小程序。1. 菜谱微信小程序源码云开发版直接导入就能跑省掉服务器那层麻烦想给餐饮店做一个菜谱小程序问了一圈服务器最便宜也要几十块一个月还要备案。这份仿京细菜谱的小程序源码走的是微信云开发路线不需要域名、不需要服务器导入微信开发者工具就能跑。我拆了一遍它的分类做得很细八大菜系、人群、场合、口味、功效全都有索引适合做毕业设计、接外包底子也适合想研究云数据库在小程序里怎么落地的开发。下面我会从文件结构、导入配置、数据流和踩坑几个角度把它完整摊开照着操作可以快速复现。2. 先拆源码文件职责、云开发原理和集合设计2.1 从 JS 文件名推断页面职责拿到压缩包后先别急着导入。我看到的项目正文列出的文件有ascf.jpg、share.jpg、cover_null.jpg和一批 JSindex.js、list.js、recipe.js、user.js、mypage.js、mypage1.js。从这个文件分布看基本可以确定是微信小程序原生项目不是 uniapp 或 taro。uniapp 项目通常会有pages.json、App.vue、main.js而原生小程序是每个页面四个同后缀文件。按命名习惯推测index.js对应首页负责展示分类导航和推荐菜谱list.js对应分类菜谱列表页会接收categoryId参数做分页读取recipe.js对应菜谱详情页一般用options.id查询单个菜谱user.js负责用户信息相关逻辑比如读取头像昵称、本地缓存mypage.js和mypage1.js是个人中心可能是不同版本的页面实际生效路由看app.json的pages数组。这里有个判断技巧不要只看 JS 名字要打开app.json确认pages数组的顺序。数组第一项是首页后面依次是其他页面。如果mypage1.js不在pages数组里说明它只是被mypage.js引用的公共模块或者已经废弃的旧页面。做二次开发时删除文件前先看路由避免删掉还在使用的页面导致编译报错。share.jpg是转发分享时用的封图cover_null.jpg应该是菜谱没有封面时的兜底图ascf.jpg从名字看可能是广告位或头部轮播图。图片资源不多的话要留意 image 标签的路径如果引用的是本地相对路径在分包或发布前别乱移动。2.2 云开发替代了传统后端的三层结构很多第一次接触云开发的人把它当成“小程序的数据库”不够准确。微信云开发其实是一个 BaaS提供了数据库、云存储、云函数三件套。对这份菜谱源码来说菜谱记录和分类信息放在云数据库菜谱图片放在云存储用户身份和收藏逻辑可以不写云函数直接用内置的_openid字段区分。也就是说常规小程序开发里需要自己买服务器、写接口、处理文件上传下载、申请 HTTPS 域名和备案的那一层云开发全部接管了。这也是摘要里强调“不需要域名和服务器即可搭建”的原因。不用云开发、用传统后端的话流程是这样的购买轻量服务器、部署接口、申请 HTTPS 证书并备案、小程序后台配置 request 合法域名、联调接口。每一步都可能卡几天尤其备案要等审核。云开发版本把这些步骤压缩成两步创建云环境、初始化app.js。对个人开发者来说省去的不只是钱还有时间成本。但要注意云开发不是完全没有服务器概念它只是把底层运维隐藏了。你的数据仍然存在云服务商的服务器上底层资源有免费额度超过后按量付费。个人菜谱小程序的访问量不大通常不会超但上线后要关注控制台的用量指标避免月底收到超额账单。2.3 初始化代码与数据库集合字段设计云开发环境初始化一般在app.js的onLaunch里。下面是一段兼容写法// app.js App({ onLaunch() { if (!wx.cloud) { console.error(请使用 2.2.3 以上基础库以支持云开发); return; } wx.cloud.init({ env: cloud1-3g9t0abc, // 替换成你自己的云环境 ID traceUser: true }); } });代码里env的值决定所有数据库、存储访问落在哪个环境。环境 ID 在云开发控制台首页可以看到格式通常是cloud1-xxxxxxxx。如果你不填env默认使用第一个创建的环境但多人协作时很容易连到别人的环境所以我建议每次都显式写上。接下来说集合设计。菜谱类小程序至少需要两张表dishes和categories。dishes表的核心字段我整理如下字段类型说明_idstring云数据库自动生成详情页传参用namestring菜名用于列表展示和搜索categoryIdstring分类 ID关联categories表imagestring云存储 fileID也可以是 https 链接ingredientsarray食材列表比如[五花肉 500g, 冰糖 30g]stepsarray步骤列表按顺序排列heatnumber热量或制作难度可选createTimedate入库时间分页排序用为什么步骤和食材都不用单独建表因为云开发数据库是文档型存数组拆取很方便详情页一次.get()就能拿到所有渲染数据。如果像 MySQL 那样拆成ingredients表、steps表小程序端还得分两次查询再拼接完全没必要。这也是一线开发里的常见误区把后端数据库思维直接搬进小程序会导致性能差、代码啰嗦。2.4 数据权限先想清楚谁能读、谁能写在云开发控制台新建集合时默认权限是“仅创建者可读写”。对于菜谱这种公共内容这个权限会让非创建者看到空数据。你需要手动改成“所有用户可读仅创建者可写”。这样游客打开小程序能读到所有菜谱但数据库中的文档不会被随意篡改。这里有一个细节如果要做收藏功能收藏表favorites应该继续用“仅创建者可读写”。因为云数据库在写入时会自动给每条记录打上_openid字段表示创建者身份。用户 A 收藏一条菜生成一条带 A 的 openid 的文档用户 B 收藏是他自己的文档互相不冲突。查询收藏列表时用.where({ _openid: {openid} })就能拿到当前用户的数据。这段逻辑不需要自己调用登录云函数是云开发默认行为。如果你以后加了管理员修改菜谱的需求管理员操作通常要走云函数因为小程序端受权限限制无法修改其他人的文档。云函数端拿到管理员 openid 后可以绕过权限校验对任意文档做更新。这个放在后面进阶部分讲。3. 落地导入开发者工具、开通云环境并导入数据3.1 解压后先做三件事第一件事就是解压不要用在线压缩工具因为里面可能有中文文件和嵌套目录解压不完整会导致导入报错。第二件事检查文件完整性。一个原生小程序项目至少要有的文件是app.js、app.json、project.config.json。缺少project.config.json时开发者工具无法识别项目类型会提示“请选择正确的项目目录”。第三件事看project.config.json里的appid。如果里面写的是touristappid或别人的 AppID导入后云开发功能用不了必须改成你自己的。不要直接双击打开文件用 VS Code 打开整个项目按 CtrlShiftF 搜索appid替换成自己的。注意有些模板会把 AppID 也写在app.js里要一起替换。3.2 注册小程序并开通云开发在小程序公众平台注册一个小程序账号个人主体也可以注册。注册成功后拿到 AppID。然后打开微信开发者工具点击“导入项目”选择解压后的目录AppID 填自己刚申请的。后端服务选择“小程序·云开发”点确定。导入成功后工具栏会出现“云开发”按钮。首次点开会要求创建环境。环境 ID 形如cloud1-xxxx随便起名但记住它后面代码要用。计费模式建议选“按量付费”个人项目即使有量也很低按量付费比包月划算。如果暂时没开通创建免费额度环境也可以但要注意免费环境有时效和资源限制。3.3 在云开发控制台创建集合云环境创建好后进入“数据库”页面新建两个集合集合名必须和代码一致dishes菜谱主表categories分类表如果源码里用的集合名不是这两个你在控制台的新建名字要和源码里的.collection(xxx)对应。我拿到一个模板时会先在代码里搜索collection(把出现过的集合名列出来再去控制台建。这是最快的方式。集合权限设置dishes和categories都选“所有用户可读仅创建者可写”。不要选“仅创建者可读写”否则非管理员用户打开就是空白。这个是菜谱类的公共数据场景跟收藏表不一样。3.4 导入菜谱数据JSON 文件格式注意数据库支持导入 JSON 或 CSV。最常见的格式是一个 JSON 数组每个对象是一条记录。下面这个示例可以导入dishes[ { name: 西红柿炒鸡蛋, categoryId: home, image: cloud://cloud1-xxxx.636c-cloud1-xxxx-1300000000/recipe/tomato.jpg, ingredients: [西红柿 2个, 鸡蛋 3个, 盐 5g, 糖 3g], steps: [西红柿切块, 鸡蛋打散炒熟, 下西红柿翻炒, 调味出锅], createTime: 2025-01-01T00:00:0008:00 }, { name: 红烧肉, categoryId: re_cai, image: cloud://cloud1-xxxx.636c-cloud1-xxxx-1300000000/recipe/hongshao.jpg, ingredients: [五花肉 500g, 冰糖 30g, 生抽 20ml, 姜 3片], steps: [五花肉切块焯水, 炒糖色, 下肉块上色, 加调料炖40分钟], createTime: 2025-01-02T00:00:0008:00 } ]导入时有几个注意点数组最外层别忘了方括号文件编码保持 UTF-8不要带 BOM。_id字段不要手动写导入时会自动生成导入后再从控制台复制_id去关联分类。createTime建议用 ISO 字符串方便后面orderBy排序和startAfter分页。如果图片还没上传云存储可以先填空字符串后面用update方法补上去。导入完成后在集合里看到这些记录基本就成功一半了。3.5 云存储传图并回填 fileID接下来把菜谱图片上传到云存储。进入云开发控制台“存储”页面创建recipe目录批量上传图片。上传完后在文件列表点“复制文件ID”会得到类似cloud://cloud1-xxxx.636c-cloud1-xxxx-1300000000/recipe/tomato.jpg的路径把这个值填到dishes记录里的image字段。这里有个容易搞混的概念云存储 fileID 和外链 URL 的区别。fileID 是云开发内部的引用小程序端image标签可以直接用。而外链 URL 需要在微信后台配置 downloadFile 合法域名如果是http://或非 https 还会被拦截。所以能传云存储就传云存储不要转外链省去域名白名单配置。如果数据较多不希望手动回填可以在控制台用导出、修改再导入的方式批量操作。但建议先小批量试通再全量操作。3.6 真机预览前要做的检查打开开发者工具的“编译”按钮模拟器如果能显示首页但不显示菜谱先打开 Console 看报错。常见问题集合名不存在、环境 ID 不对、集合权限太严格。确认无误后点击“预览”用微信扫码真机调试。真机和模拟器的差异通常体现在图片和网络请求多准备一台安卓和一台 iPhone 测试。到这里一个可跑的菜谱小程序就搭起来了。下一步我把列表页和详情页的核心代码逻辑讲透方便你按自己需求改。4. 核心逻辑列表分页、详情跳转与用户页背后的数据边界4.1 列表页用 skiplimit 加载更多注意 20 条上限list.js是菜谱列表页它的核心是加载对应分类下的一页菜谱。一段常见的基础实现如下// list.js const db wx.cloud.database(); const PAGE_SIZE 10; let page 0; let isFetching false; let hasMore true; Page({ data: { dishes: [], categoryId: }, onLoad(options) { this.setData({ categoryId: options.categoryId || }); this.loadList(); }, loadList() { if (isFetching || !hasMore) return; isFetching true; wx.showLoading({ title: 加载中 }); const query db.collection(dishes); if (this.data.categoryId) { query.where({ categoryId: this.data.categoryId }); } query.skip(page * PAGE_SIZE) .limit(PAGE_SIZE) .get() .then(res { const newList this.data.dishes.concat(res.data); this.setData({ dishes: newList }); page; if (res.data.length PAGE_SIZE) { hasMore false; } wx.hideLoading(); }) .catch(err { console.error(err); wx.hideLoading(); }) .finally(() { isFetching false; }); }, onReachBottom() { this.loadList(); } });这段代码里有几个参数说明PAGE_SIZE是每页数量这里写 10实际可以调大到 20不要超过 20。云开发数据库在普通小程序端单次get()最多返回 20 条设置 50 也没用后台会截断。isFetching是并发锁防止 onReachBottom 在数据还没返回时又触发一次导致重复请求。hasMore用于判断是否还有下一页如果返回条数小于 PAGE_SIZE说明已经到最后一页。where({ categoryId })是精确匹配菜谱的categoryId必须和分类表_id一致。这个写法对数据总量不超过 1000 条的菜谱项目够用。如果以后数据量大了skip会因为扫描偏移量过大而变慢到时改成orderBy(createTime, desc)加startAfter(res.data[res.data.length - 1].createTime)的方式。这个我们放到第 5 章避坑里详细展开。4.2 触底加载更多onReachBottom 与页面配置“微信小程序页面列表加载更多”这个热搜问题本质上就是两件事触发条件和翻页逻辑。触发条件除了onReachBottom还需要在页面的.json里开启{ onReachBottomDistance: 50, enablePullDownRefresh: true }onReachBottomDistance是距离底部多少像素时触发默认 50不用改也行。enablePullDownRefresh是下拉刷新如果你不需要下拉刷新可以直接删掉。然后下拉刷新的处理函数onPullDownRefresh() { page 0; this.setData({ dishes: [], hasMore: true }); this.loadList().finally(() { wx.stopPullDownRefresh(); }); }注意onPullDownRefresh里必须先重置page和dishes否则你会看到旧数据拼接新数据列表越来越长。这个细节做外包时经常被我拿来调 bug。4.3 首页分类点击跳转与参数接收首页index.js里通常有一段wx.navigateTogoList(e) { const categoryId e.currentTarget.dataset.id; wx.navigateTo({ url: /pages/list/list?categoryId categoryId }); }dataset.id是 WXML 里>view classcategory-item>// recipe.js const db wx.cloud.database(); Page({ data: { dish: {} }, onLoad(options) { const id options.id; if (!id) return; db.collection(dishes).doc(id).get() .then(res { this.setData({ dish: res.data }); }) .catch(err console.error(err)); } });doc(id)是指定记录 ID 直接读取比where({_id: id})更高效。拿到记录后dish.steps是一个数组WXML 里用wx:for渲染步骤view classsteps view wx:for{{dish.steps}} wx:keyindex classstep-item text{{index 1}}. {{item}}/text /view /viewwx:keyindex用于列表复用时的 key这里因为 steps 数组的元素可能重复用index做 key 是可以接受的。注意不要写成wx:key*item以免字符串重复导致警告。4.5 用户页与云开发的 openid 机制user.js和mypage.js最常做的操作是展示用户登录信息和收藏列表。云开发下识别用户的默认方式不是自己写登录而是读取云数据库记录里的_openid字段。小程序端调用collection.add时云平台会自动把当前用户的openid写入_openid。查询时用db.collection(favorites) .where({ _openid: {openid} }) .get() .then(res { this.setData({ favorites: res.data }); });这里{openid}是云开发的特殊语法在服务端或小程序端查询时会被自动替换成当前用户 openid。因此收藏功能完全不用自己写云函数。但如果需要把收藏与用户的其他信息关联比如昵称头像可能需要在用户第一次授权时保存一份 user 文档。此时注意wx.getUserProfile在最新基础库上返回的昵称是“微信用户”默认昵称头像也是灰色默认头像真实头像需要用户上传或使用开放数据这个不是源码问题是微信平台策略。这一段主要讲了云开发数据库的分页限制、页面跳转参数、详情读取、用户 openid 自动注入。理解这四点修改模板就有方向了。5. 常见坑与排查环境、分页、图片和权限5.1 数据空白类环境 ID 和集合名不一致现象模拟器打开首页分类能显示但点进列表加载不出任何数据console 提示collection not exists或Collection not found或者干脆没有报错。原因一种是app.js里的env没有改成自己的云环境 ID代码访问的是别人创建的环境那个环境里没有你的集合。另一种是项目里写的集合名是recipe但你在控制台建的集合叫dishes。我在处理资源时见过把.collection(recipe)写成.collection(dishes)的就是复制时没改名称。解决打开开发者工具的 Console定位到报错信息里的集合名去云开发控制台创建同名集合再确认env正确。如果env显示undefined回到app.js里显式写上环境 ID。改完重启编译。5.2 分页失效类skip 超过记录数导致空白现象列表第一页正常下拉加载更多后第二页偶尔有数据到第三页或更后直接空白甚至报错Error: errCode: -502005 database request fail。原因skip分页在记录总数超过 1000 条时数据库会拒绝请求另外PAGE_SIZE如果设置为 50而数据库单次最多返回 20 条第二页的skip就会跳过 40 条结果只返回后面 10 条看起来就像缺数据。或者hasMore判断逻辑写错了导致一直请求。解决把PAGE_SIZE改回 20 以内将分页方式改为基于游标。游标分页示例let lastCreateTime null; function loadMore() { let query db.collection(dishes).orderBy(createTime, desc).limit(20); if (lastCreateTime) { query query.startAfter(lastCreateTime); } query.get().then(res { if (res.data.length) { lastCreateTime res.data[res.data.length - 1].createTime; this.setData({ dishes: this.data.dishes.concat(res.data) }); } else { this.setData({ hasMore: false }); } }); }这里的startAfter接收排序字段的值必须是上次返回的最后一条记录的createTime且排序字段必须已经建索引。索引在控制台数据库中创建字段选择createTime排序方式选“降序”。5.3 图片裂图类云存储 fileID 被当网络链接处理现象模拟器上图片显示正常真机预览一部分图片显示空白或控制台出现url not in domain list。原因如果image字段填的是https://外链微信小程序要求下载域名必须加入白名单且必须是备案过的 HTTPS。如果填的是云存储 fileID默认是可以直接显示的不需要白名单但如果使用了旧环境产生的 fileID新环境里无法识别就会裂图。解决排除法先看image值开头是不是cloud://如果开头是http去小程序后台配置 downloadFile 合法域名或者把图传到云存储换成 fileID。如果已经是cloud://还是显示不了大概率是环境不匹配重新上传一次图片并替换image字段。还有一个小坑云存储目录名有中文或空格复制 fileID 后路径会被编码建议目录名全英文。5.4 编译失败类基础库版本过低和找不到 appid现象导入后编译直接报wx.cloud is undefined或Cannot read property init of undefined点云开发按钮没反应。原因微信开发者工具的基础库版本低于 2.2.3wx.cloudAPI 不存在或者 AppID 是测试号测试号无法开通云开发。解决在开发者工具“详情-本地设置”中把调试基础库切到最新稳定版。AppID 换成正式小程序的测试号必须替换成自己的。个人主体注册的小程序也可以开通云开发不需要企业资质。注意基础库设置只影响当前项目不要全局更改。5.5 权限太严类数据导入后其他用户看到空白现象自己在控制台导入的菜谱数据自己用开发者工具能看到但用其他微信号扫码预览列表是空的。原因集合权限是“仅创建者可读写”控制台导入的数据创建者是管理员普通用户没有读权限。云开发数据库权限是集合级别的不是每条记录单独设置。解决把dishes和categories两个集合的权限改成“所有用户可读仅创建者可写”。这一步必须在控制台手动操作不是改代码。改完后让同事或另一个微信号扫码测试。注意如果之后又用控制台重新导入数据权限不会重置但如果新建集合默认权限又变成“仅创建者可读写”这是最容易漏的地方。这五条基本覆盖了这个模板从导入到真机预览的高频问题。按“现象-原因-解决”排查大多数情况 10 分钟内能定位。6. 进阶把菜谱模板改成自己风格的三个小技巧第一个搜索功能。菜谱数据量不大时用模糊查询const db wx.cloud.database(); db.collection(dishes) .where({ name: db.RegExp({ regexp: keyword, options: i }) }) .limit(20) .get()正则查询无法建索引几万条以内没问题再大就要用云函数接入搜索服务。第二个默认导航栏够用就别自定义。这个模板用的是系统导航栏在app.json的window里改navigationBarTitleText、navigationBarBackgroundColor就行。如果你非要自定义设置navigationStyle: custom后要自己处理wx.getMenuButtonBoundingClientRect算胶囊高度容易在安卓和 iOS 上出现偏差。我的经验是不是被产品逼到那份上别动导航栏。第三个分享图已经准备好share.jpg可以直接用于onShareAppMessageonShareAppMessage() { return { title: 这几道家常菜厨房新手也能做, imageUrl: /images/share.jpg, path: /pages/index/index }; }注意imageUrl要写本地路径如果写网络链接需要配域名白名单。最后说一句自己的教训。我以前接外包用类似模板就是第 5 章那个分页坑客户上线后数据量到 200 条用户在列表页划到一半就空白我排查了一整天才定位到是limit超过云开发限制和skip超界。从那以后我每次做云开发列表页都会先看数据量上限再决定用哪种分页然后强制在真机上把列表翻到底。如果你准备下载这份源码来改成自己的菜谱小程序建议先在自己账号下按第三章流程跑通再开始改界面能少折腾一个下午。希望帮到你。本文还有配套的精品资源点击获取