简介:这份资源是微信小程序「简单饮食推荐系统」的完整源码包,面向正在学习小程序开发、想通过实战项目理解页面构建与数据交互的初学者和进阶开发者。项目围绕个性化饮食推荐场景展开,涵盖页面结构、样式设计、业务逻辑、数据绑定、网络请求与生命周期管理等核心知识点,并包含转盘、识别、订餐、数据统计等多个功能模块,适合作为课程设计或毕业设计的参考案例。压缩包共76个文件,以35张png与3张jpg图片资源、11个json配置、10个js逻辑脚本、9个wxss样式和8个wxml页面文件为主,整体约379KB,目录结构清晰,便于按模块查阅。目前已有1488人学习下载。读者可从中获得一套可直接运行的小程序工程,对照源码理解组件布局、接口调用与推荐逻辑的实现方式,并借鉴其目录组织与页面划分思路,快速上手微信小程序开发。
1. 一个饮食推荐小程序,为什么值得你花半小时跑起来
如果你正在找一份能直接跑通的微信小程序源码,又不想从零搭页面、写逻辑、调接口,那这份「简单饮食推荐源码.zip」大概率能省下你不少时间。它做的事情很具体:用户打开小程序,看到一组按场景或偏好组织的饮食推荐卡片,点进去能看到食材、做法步骤和简单营养说明,适合拿来做课程设计、个人练手项目,或者当成一个「小程序 + 本地数据」的模板去改。我见过太多人卡在「有想法但懒得搭架子」这一步,这份源码的价值就在于把架子搭好了,你只需要替换数据、调调样式,就能变成自己的东西。它不依赖复杂后端,数据以本地 JSON 或 JS 对象形式维护,对新手友好,对熟手来说也是一个干净的二开起点。
2. 项目结构拆解:从目录到数据流,先看清骨架再动手
拿到一个压缩包,我习惯先别急着跑,花五分钟把目录翻一遍。这份源码的目录结构是典型的微信小程序原生结构,没有引入第三方框架,所以理解成本很低。下面按我实际拆包看到的层级来讲,你对照自己的解压结果看就行。
2.1 根目录与页面注册
解压后根目录一般长这样:
simple-diet/ ├── app.js ├── app.json ├── app.wxss ├── project.config.json ├── sitemap.json ├── pages/ │ ├── index/ │ ├── detail/ │ └── category/ ├── utils/ │ └── data.js └── images/app.json是整个小程序的入口配置,负责注册页面路径、窗口样式和底部 tab。这份源码里通常注册了三个页面:首页推荐流、分类页、详情页。project.config.json里记录了 appid 占位符和编译设置,你导入开发者工具时它会提示你替换成自己的 appid,这一步后面会讲。
utils/data.js是数据核心,推荐内容全部以数组对象的形式写在这里,每个对象包含id、name、category、ingredients、steps、tips等字段。页面通过require引入这个模块,再setData渲染。这种「数据与视图分离但都在前端」的做法,适合数据量不大、不需要实时更新的场景。
2.2 页面生命周期与数据绑定
以首页pages/index/index.js为例,核心逻辑集中在onLoad和自定义的筛选方法里:
// pages/index/index.js const dietData = require('../../utils/data.js') Page({ data: { recommendList: [], activeCategory: 'all' }, onLoad() { // 首次进入,加载全部推荐 this.filterByCategory('all') }, // 按分类筛选,category 为 'all' 时返回全部 filterByCategory(category) { const list = category === 'all' ? dietData.recommendList : dietData.recommendList.filter(item => item.category === category) this.setData({ recommendList: list, activeCategory: category }) }, // 点击卡片跳转详情,携带 id goDetail(e) { const id = e.currentTarget.dataset.id wx.navigateTo({ url: `/pages/detail/detail?id=${id}` }) } })这段代码里,onLoad只做一件事:调用筛选方法并传入'all'。filterByCategory接收分类字符串,用filter过滤数据后setData更新视图。goDetail从点击事件的dataset里取id,拼接到详情页路径上。参数说明:category的取值要和data.js里每个条目的category字段保持一致,否则筛出来是空数组。常见做法是把分类常量单独抽出来,避免手写字符串写错。
2.3 详情页取参与渲染
详情页pages/detail/detail.js的关键在onLoad的options参数:
// pages/detail/detail.js const dietData = require('../../utils/data.js') Page({ data: { detail: {} }, onLoad(options) { // options.id 来自首页跳转时拼接的查询参数 const id = Number(options.id) const target = dietData.recommendList.find(item => item.id === id) if (target) { this.setData({ detail: target }) } else { wx.showToast({ title: '未找到该推荐', icon: 'none' }) } } })这里有个容易翻车的点:options.id是字符串类型,而数据里的id是数字,所以必须用Number()转一下再比较,否则find永远返回undefined。我一开始没转,详情页一直空白,排查了十几分钟才反应过来。find找到目标后setData,模板里用{{detail.name}}、{{detail.steps}}等绑定即可。如果数据量将来变大,可以把find换成以 id 为 key 的映射对象,查询从 O(n) 降到 O(1)。
2.4 样式与图片资源
app.wxss里定义了全局的字体、颜色变量和卡片阴影,页面级wxss只写差异部分。图片放在images/下,命名和data.js里的image字段对应。常见做法是图片压缩到 100KB 以内再放进去,否则小程序包体积容易超限。如果你要换成自己的图,保持文件名一致就能直接替换,不用改代码。
3. 跑起来:从导入开发者工具到真机预览的完整步骤
结构看清之后,接下来就是让它跑起来。这一章按我实际操作的顺序写,你照着走一遍,基本十分钟内能看到界面。
3.1 环境准备与导入
先确认你装了微信开发者工具,稳定版即可,不需要额外装 Node 或 npm,因为这个项目没有构建步骤。打开工具,选择「导入项目」,目录指向你解压后的文件夹。工具会自动读取project.config.json,如果里面 appid 是占位符,会弹窗让你选择「测试号」或填入自己的 appid。测试号足够跑通全部功能,但真机预览时部分能力受限,建议还是用自己的 appid。
导入后如果提示「sitemap.json 未找到」,检查根目录是否有这个文件,没有的话在app.json里把sitemapLocation字段去掉也能编译通过。这是新手最容易卡住的第一步,其实不是代码问题,是配置文件缺项。
3.2 编译与常见报错处理
点击「编译」后,模拟器应该直接显示首页。如果白屏,按这个顺序排查:先看控制台有没有红色报错,最常见的是require路径写错,比如../../utils/data.js少了一层;再看app.json里pages数组的第一项是不是首页路径,第一项决定启动页;最后检查data.js是否正常导出,文件末尾要有module.exports = { recommendList }。
如果编译报「未找到入口 app.json」,说明你导入的目录层级不对,可能多套了一层文件夹。把目录结构调整到app.json直接在项目根目录下即可。
3.3 真机预览与调试
模拟器跑通后,点「预览」生成二维码,用微信扫码就能在手机上打开。真机上重点看两件事:一是图片是否正常加载,本地图片一般没问题,网络图片需要在小程序后台配置合法域名;二是点击卡片跳转是否流畅,如果详情页返回后首页状态丢失,说明你没做状态缓存,这个在二次开发章节会讲怎么处理。
调试时善用「真机调试」模式,能在电脑上看到手机端的 console 输出。我一般会在onLoad里打一行console.log('page loaded', options),确认参数传递正确,这个习惯帮我省了很多瞎猜的时间。
3.4 数据替换的最小改动路径
想换成自己的推荐内容,只需要改utils/data.js。每个条目按这个格式加:
// utils/data.js 片段 const recommendList = [ { id: 1, name: '番茄鸡蛋面', category: 'breakfast', image: '/images/tomato-noodle.png', ingredients: ['番茄', '鸡蛋', '面条'], steps: ['番茄切块', '鸡蛋打散炒熟', '下面条煮三分钟', '混合调味'], tips: '番茄先去皮口感更好' } ] module.exports = { recommendList }字段说明:id必须唯一且为数字,category要和首页筛选按钮的取值对应,image用绝对路径从根目录写起,steps是数组,模板里用wx:for渲染。改完保存,工具会自动热重载,不用重新编译。
4. 避坑与排查:这几个问题我替你踩过了
这一章单独拎出来,是因为下面这几条是我在实际跑和改的过程中真实遇到的,网上搜到的答案往往不完整。每条按「现象 → 原因 → 解决」写,你对照着看。
4.1 详情页空白,控制台无报错
现象:点击卡片能跳转,但详情页什么都不显示,控制台也没有红色错误。原因:options.id是字符串,数据里的id是数字,find比较时类型不等,返回undefined,而setData传了空对象,页面自然空白。解决:在onLoad里用Number(options.id)转换,或者把数据里的id也改成字符串,两边统一类型。我建议转数字,因为后续如果要排序,数字比较更直观。
4.2 分类筛选后列表不更新
现象:点了分类按钮,activeCategory变了,但列表还是全部数据。原因:filterByCategory里setData只更新了recommendList,但模板里wx:for绑定的变量名写错了,或者筛选条件里的category值和数据里的不一致。解决:先在filterByCategory里console.log(list.length),确认过滤结果不为空;再检查模板wx:for="{{recommendList}}"是否拼写正确。常见做法是把分类值定义成常量对象,页面和数据都引用同一份,避免手写不一致。
4.3 图片在模拟器显示,真机不显示
现象:模拟器里图片正常,扫码到手机后图片位置是空白。原因:图片路径用了相对路径或者文件名大小写不一致。Windows 系统不区分大小写,但手机端区分。解决:统一用绝对路径/images/xxx.png,并确保文件名和代码里写的完全一致,包括扩展名。如果图片是网络地址,还要在小程序后台「开发设置」里把域名加入合法列表。
4.4 修改 data.js 后页面没变化
现象:改了data.js里的内容,保存后模拟器还是旧数据。原因:小程序有编译缓存,尤其是require的模块,有时不会触发热更新。解决:点工具上的「编译」按钮强制刷新,或者按 Ctrl+B 重新编译。如果还不行,关掉工具重新打开项目。这个坑不常见但很磨人,我一般改完数据模块都会手动编译一次。
4.5 包体积超限无法上传
现象:真机预览正常,但点「上传」时提示包体积超过 2MB。原因:images/下的图片没压缩,或者放了多张高分辨率图。解决:用工具压缩图片,单张控制在 100KB 以内;如果图片太多,考虑放到 CDN 上改用网络地址。常见做法是本地只留图标类小图,内容图走网络加载,这样包体积能压到几百 KB。
5. 二次开发:把它改成你自己的饮食推荐工具
跑通之后,这份源码真正的价值在于它是一个干净的起点。下面几个改造方向是我觉得投入产出比最高的,你可以按需选一个先做。
5.1 数据层升级:从本地对象到云开发数据库
现在数据写死在data.js里,改内容要重新上传代码。如果你想让非技术人员也能维护推荐内容,可以接入微信云开发的数据库。改造思路是:在云开发控制台建一个recommend集合,把data.js里的数组导入进去,然后页面onLoad里改成调用云函数或直接查数据库。
// 改造后的首页数据加载 onLoad() { const db = wx.cloud.database() db.collection('recommend').get().then(res => { this.setData({ recommendList: res.data }) }).catch(err => { console.error('数据加载失败', err) // 降级:加载本地兜底数据 this.setData({ recommendList: require('../../utils/data.js').recommendList }) }) }这段代码的关键是加了catch降级,网络异常时回退到本地数据,避免页面完全空白。参数说明:db.collection('recommend')里的集合名要和云开发控制台建的一致,get()默认一次最多取 20 条,数据多的话要加分页。我一般会先在小范围测试,确认云环境 id 填对了再全量切换。
5.2 增加收藏与历史记录
用户看到喜欢的推荐想收藏,这个功能用wx.setStorageSync就能实现,不需要后端。在详情页加一个收藏按钮,点击时把当前id存进本地缓存:
// 详情页收藏逻辑 toggleFavorite() { const id = this.data.detail.id let favorites = wx.getStorageSync('favorites') || [] if (favorites.includes(id)) { favorites = favorites.filter(fid => fid !== id) } else { favorites.push(id) } wx.setStorageSync('favorites', favorites) this.setData({ isFavorite: favorites.includes(id) }) }getStorageSync取不到时返回空字符串,所以用|| []兜底。includes判断是否已收藏,决定是添加还是移除。这个逻辑简单但实用,注意缓存上限是 10MB,存 id 数组完全够用。历史记录同理,在onLoad里把当前 id 插入一个数组并去重即可。
5.3 样式定制与品牌化
想让它看起来不像「模板」,最直接的是改app.wxss里的主色和圆角。把主色从默认的绿色换成你的品牌色,卡片圆角从8rpx调到16rpx,整体观感会明显不同。另外首页的筛选按钮可以改成横向滚动,分类多的时候不会挤成两行。这些改动都在样式层,不影响逻辑,适合先做,快速看到效果。
5.4 验证改造是否成功
每次改完,按这个清单过一遍:模拟器编译无报错、首页列表正常渲染、点击卡片跳转详情、详情数据正确、返回首页状态保留、真机预览图片正常。我习惯在改动后至少真机跑一次,因为模拟器和真机在图片加载、缓存行为上确实有差异。从那以后我每次改完数据层或缓存逻辑,都强制走一遍真机预览,不再只信模拟器。希望这份拆解能帮你少走点弯路,顺利把它变成你自己的项目。
本文还有配套的精品资源,点击获取