简介:这套微信小程序菜谱源码开箱就能用,不用改配置直接导入开发者工具就能运行。首页、发现页、分类列表页、菜谱详情页、搜索页和结果页都已写好,页面之间跳转正常,路径在app.里全注册好了。支持按菜系或口味分类浏览,也能输关键词搜菜名,每道菜带图、步骤、用料说明。代码是原生写法,用标准app.js/app./app.wxss三件套,utils里有常用工具函数,res目录放了图片、GIF等资源,临时资源也打包好了。菜谱数据放在pages/content和pages/list里,用本地模拟方式加载,没依赖后端,适合练手页面渲染、setData绑定、wx:for列表循环、事件传参这些基础操作。想加用户收藏、评论或者接入真实API,结构清晰容易扩展,新手学完能上手做美食类小程序。
1. 这套菜谱小程序源码,为什么值得你花30分钟导入调试?
我带过不少刚接触小程序开发的朋友,他们常卡在“第一个能跑起来的项目”上——不是写不出代码,而是不知道从哪下手、改哪行、为什么报错、数据怎么连上页面。这套菜谱源码,就是我当年给新人布置的“第一课作业”,也是我现在团队新人入职前三天必跑通的练手项目。它不炫技、不堆功能,但把微信小程序最核心的开发逻辑全摊开了:页面怎么注册、数据怎么从 JS 流到 WXML、样式怎么分层管理、搜索怎么传参、分类怎么联动、图片资源怎么引用……全都藏在你看得见摸得着的文件里。
关键词里提到的“微信小程序”“菜谱源码”“分类浏览”“菜谱搜索”,不是空泛标签,而是这套代码每天真实运行的四个支点:首页轮播图+推荐菜系入口 → 发现页按“川菜/粤菜/家常菜”或“辣/清淡/快手”双维度筛选 → 列表页展示匹配结果 → 点进去是带高清步骤图、分步文字说明、用料清单的详情页;搜索页输入“红烧”“蒸鱼”“减脂”,回车即出结果,点击跳转无缝衔接。所有交互都基于原生框架,没用任何第三方 UI 库(比如 WeUI 或 Vant),这意味着你看到的每一行 WXML 都是你将来自己写的模板,每一个 wx:for 循环、每一个 bindtap 事件、每一个 setData 调用,都是可复用、可拆解、可替换的标准写法。
它特别适合两类人:一类是完全没写过小程序的新手,你不需要懂服务器、不用配域名、不碰 HTTPS,解压→导入开发者工具→点编译→就能看到一个带图、有跳转、能搜索的完整应用;另一类是有基础但总在“页面跳转传参”“列表渲染卡顿”“图片加载失败”上反复踩坑的开发者,这套代码里埋了至少7处典型陷阱的解法——比如 wx.navigateTo 传对象参数会丢失,它用字符串拼接 + JSON.stringify 再 JSON.parse 安全落地;比如长列表滚动卡顿,它在 list 页面做了分页模拟和节流加载;比如本地图片路径写错导致空白,它在 res 目录下统一用相对路径规范管理。这些不是文档里写的“应该怎么做”,而是我实测过、上线过、被用户反馈过、再优化过的“实际怎么做”。
更重要的是,它没给你设限。你说“就这?没登录、没收藏、没评论?”——对,它故意没做。因为加这些功能的前提,是你先搞懂 app.js 里的全局状态怎么初始化、pages/content/content.js 里怎么监听用户点击并触发收藏动作、utils/request.js 里怎么封装一个带 loading 的 API 请求函数。这套源码就像一张干净的施工图纸,地基、承重墙、水电管线都标得清清楚楚,你要盖几层楼、装什么装修风格,全由你定。我见过学员用它三天做出带用户收藏夹的版本,也见过团队把它改成企业食堂订餐系统,底层结构一动没动,只替换了数据源和部分样式。
所以别把它当成“一个菜谱小程序”,它是一套可执行的小程序开发说明书,是微信官方文档之外,最贴近真实工作流的入门沙盒。
2. 整体架构设计与模块拆解:为什么这样组织页面和数据?
2.1 页面结构为何采用“首页-发现-列表-详情-搜索”五段式?
这套源码的页面路由设计,不是凭空画出来的,而是严格遵循微信小程序官方推荐的“主流程闭环”原则:用户打开小程序 → 看到核心内容入口(首页)→ 主动探索更多(发现页)→ 筛选目标(分类/搜索)→ 查看具体信息(详情页)→ 返回继续操作(页面栈管理)。我们来拆开看每个页面的不可替代性:
-
首页(pages/index/index):承担“冷启动首屏”任务。它不承载复杂逻辑,只做三件事:① 展示轮播图(
<swiper>组件,自动播放+指示点);② 提供“今日推荐”“人气菜谱”两个卡片区块,每张卡片绑定bindtap事件跳转到对应详情页;③ 底部固定导航栏(tabBar),链接到首页、发现页、我的(占位页)。这里的关键细节是:轮播图数据来自pages/index/index.js中的data对象,而非请求接口,降低首次加载延迟;卡片跳转用wx.navigateTo({ url: '/pages/content/content?id=123' }),id 是硬编码模拟值,为后续接入真实数据留好接口。 -
发现页(pages/discovery/discovery):解决“我不知道想吃什么”的场景。它提供两套平行筛选体系:左侧垂直分类栏(菜系:川菜、粤菜、鲁菜…;口味:辣、甜、咸鲜…),右侧横向滚动标签(“快手菜”“减脂餐”“宝宝辅食”…)。点击任一标签,列表页(
pages/list/list)会接收参数并刷新数据。这里的设计巧思在于:分类数据不是写死在 WXML 里,而是定义在utils/category.js中,用数组导出,便于后期增删;标签点击事件统一绑定在<view class="tag" bindtap="onTagTap" data-type="{{item.type}}" data-value="{{item.value}}">上,通过e.currentTarget.dataset提取参数,避免为每个标签写独立事件函数。 -
列表页(pages/list/list):是整个流程的“数据中转站”。它不主动拉数据,而是被动接收上一页传来的
type(如"cuisine")和value(如"chuancai"),然后从本地模拟数据池(pages/list/data.js)中过滤匹配项。关键实现是onLoad生命周期函数里解析options参数,并调用this.setData({ listData: filteredList })渲染。这里特意没用wx.request,因为新手常在这里栽跟头:忘记success回调、没处理fail状态、没加loading提示。用本地数据,你能专注看wx:for="{{listData}}"怎么把数组映射成视图,wx:key="id"怎么提升渲染性能。 -
详情页(pages/content/content):承担“信息交付”终极任务。它接收
id参数后,从pages/content/data.js中查找对应菜谱对象,渲染标题、封面图、用料清单(<view wx:for="{{ingredients}}" wx:key="index">{{item.name}} {{item.amount}}</view>)、步骤图文(<image src="{{step.image}}" mode="aspectFill"/>+<text>{{step.desc}}</text>)。这里有个易忽略点:步骤图是 GIF 格式(存于res/gif/),比静态图更能展示烹饪动态过程,而mode="aspectFill"确保图片不拉伸变形——这些细节直接决定用户停留时长。 -
搜索页(pages/search/search):实现“精准直达”。WXML 中
<input bindconfirm="onSearchConfirm" placeholder="搜菜名、食材、做法..." />绑定回车事件,JS 中onSearchConfirm(e)获取e.detail.value,调用this.search(e.detail.value)函数,在pages/search/data.js模拟数据中模糊匹配(includes()),结果传给pages/result/result页面。注意:它没做防抖,因为本地搜索毫秒级响应;但如果你要接入真实 API,这里就是加setTimeout节流的最佳位置。
这种结构不是为了“看起来多”,而是让每个页面职责单一:首页负责引流,发现页负责探索,列表页负责筛选,详情页负责呈现,搜索页负责直达。你改任何一个页面,都不会牵扯其他页面逻辑——这是可维护性的基石。
2.2 数据组织为何坚持“本地模拟 + 按页面隔离”?
很多新手一上来就想连后端,结果卡在 wx.request 的 ssl 错误、404、跨域上,半天看不到数据。这套源码反其道而行:所有数据都放在 JS 文件里,用纯对象或数组定义,不依赖任何网络请求。这不是偷懒,而是教学策略——先让你看清“数据怎么进页面”,再学“数据怎么从服务器来”。
具体怎么组织?看三个关键目录:
-
pages/list/data.js:存放所有菜谱的扁平化数组,每项含id,title,cover,cuisine,taste,duration,difficulty字段。例如:
javascript module.exports = [ { id: 1, title: "麻婆豆腐", cover: "/res/img/mabu.jpg", cuisine: "chuancai", taste: "la", duration: "25分钟", difficulty: "简单" } ]
这里cuisine和taste字段值,必须与utils/category.js中的分类键完全一致,否则筛选失效。我试过把"chuancai"写成"chuangcai",结果点击川菜标签一片空白——这就是为什么强调“键值严格匹配”。 -
pages/content/data.js:按id组织的详细数据对象,结构更深:steps数组存步骤图和文字,ingredients数组存用料,tips字符串存小贴士。例如:
javascript const contentData = { 1: { title: "麻婆豆腐", cover: "/res/img/mabu.jpg", steps: [ { image: "/res/gif/mabu1.gif", desc: "热锅凉油,下豆瓣酱炒香" }, { image: "/res/gif/mabu2.gif", desc: "加入牛肉末翻炒至变色" } ], ingredients: [ { name: "嫩豆腐", amount: "300g" }, { name: "牛肉末", amount: "100g" } ], tips: "豆腐焯水去豆腥,炒酱时火不要太大" } } module.exports = contentData -
utils/category.js:分类元数据,定义前端展示文案和数据键的映射关系:
javascript module.exports = { cuisine: [ { value: "chuancai", text: "川菜" }, { value: "yuecai", text: "粤菜" } ], taste: [ { value: "la", text: "辣" }, { value: "tian", text: "甜" } ] }
这种组织方式的好处是:修改数据不用动页面逻辑,新增菜谱只需往 data.js 里塞对象;调试时直接 console.log 查看数据结构,不用抓包看接口返回;二次开发时,把 require('../../pages/list/data') 替换成 wx.request({ url: 'https://api.xxx.com/recipes' }) 即可平滑过渡。我见过太多项目,因为数据格式混乱(比如后端返回 cuisine_name 而前端期望 cuisine),导致列表页渲染失败,最后花半天时间写转换函数——而本地模拟,让你一开始就面对最简结构。
2.3 样式与资源管理为何强调“分层 + 规范路径”?
新手常犯的错误是:图片路径写成 ./img/xxx.png,结果在真机上显示空白;或者把所有样式写在 app.wxss 里,改一个按钮颜色全站变。这套源码用三层样式体系解决:
app.wxss:只放全局重置和基础变量。例如:
```css
/ 全局重置 /
page { padding: 0; margin: 0; }
view, image, text { box-sizing: border-box; }
/ 基础变量 /
:root {
–primary-color: #ff6b35;
–border-radius: 8rpx;
}
```
这里没写任何组件样式,只为后续页面提供统一色调和圆角基准。
-
pages/xxx/xxx.wxss:页面专属样式,用 BEM 命名法(如.list__item,.list__title),确保不污染其他页面。例如pages/list/list.wxss中:
css .list__container { padding: 20rpx; } .list__item { margin-bottom: 30rpx; border-radius: var(--border-radius); overflow: hidden; } .list__cover { width: 100%; height: 200rpx; } -
res/目录:资源集中管理。里面分img/(封面图)、gif/(步骤动图)、icon/(图标)、font/(字体文件)。所有路径都用绝对路径/res/xxx引用,比如<image src="/res/img/mabu.jpg" />。为什么不用相对路径?因为小程序page组件的src解析规则是:以/开头为根目录,否则相对于当前 WXML 文件所在目录。用绝对路径,无论你在pages/index还是pages/content,引用同一张图都不会出错。
资源打包时,临时资源.zip 里已包含所有图片和 GIF,解压后直接放进 res/ 目录即可。我建议你打开 res/gif/ 看看那些 200KB 左右的 GIF——它们不是随便录的,而是用 Photoshop 优化过帧数和色彩深度,保证清晰度的同时控制体积。真机测试时,如果 GIF 加载慢,你可以把 mode="aspectFill" 改成 mode="widthFix",优先保证宽度适配,再逐步优化资源。
3. 核心功能实现详解:从搜索到分类,每一步都经得起推敲
3.1 搜索功能:如何实现“输入即搜”且不卡顿?
搜索页(pages/search/search)表面简单,背后藏着三个关键设计决策:
第一,输入框事件选择 bindconfirm 而非 bindinput。
很多教程教用 bindinput 实现实时搜索,但本地数据量少时没问题,一旦接入真实 API,频繁请求会拖垮体验。这套源码用 bindconfirm(回车键触发),既符合用户习惯(手机键盘回车即确认),又避免无效请求。WXML 中:
<input
bindconfirm="onSearchConfirm"
placeholder="搜菜名、食材、做法..."
confirm-type="search"
/>
confirm-type="search" 让键盘右下角显示“搜索”按钮,视觉引导更明确。
第二,模糊匹配算法兼顾准确与速度。
pages/search/search.js 中的 search(keyword) 函数这样写:
search(keyword) {
const allRecipes = require('../../pages/list/data')
const result = allRecipes.filter(recipe =>
recipe.title.includes(keyword) ||
recipe.ingredients.some(ing => ing.name.includes(keyword))
)
this.setData({ searchResult: result })
}
注意两点:① includes() 是 ES6 方法,兼容基础库 2.7.0+,无需 polyfill;② 不仅匹配标题,还遍历 ingredients 数组匹配食材名,提升搜索召回率。比如搜“豆腐”,能搜出麻婆豆腐、家常豆腐、皮蛋豆腐——这比只搜标题实用得多。
第三,结果页(pages/result/result)复用列表页逻辑。
pages/result/result.js 几乎和 pages/list/list.js 一样,只是 onLoad 里解析 options.q(搜索关键词)而非 type/value。这样做的好处是:你改列表页的渲染逻辑(比如加个收藏按钮),结果页自动同步,不用重复写。WXML 中 <view wx:for="{{searchResult}}" wx:key="id"> 渲染结果,和列表页完全一致。
实操时,你可能会遇到搜索无结果的情况。这时 pages/result/result.wxml 里有专门的空状态提示:
<view wx:if="{{searchResult.length === 0}}" class="empty">
<image src="/res/icon/search-empty.png" class="empty__icon"/>
<text class="empty__text">没找到 "{{q}}" 相关的菜谱</text>
</view>
这个 wx:if 条件判断,比 wx:else 更安全——因为 searchResult 可能是 undefined,length 会报错,而 === 0 是安全比较。
3.2 分类浏览:双维度筛选如何避免状态冲突?
发现页(pages/discovery/discovery)的双维度筛选(菜系 + 口味)看似简单,实则暗藏状态管理陷阱。常见错误是:点击“川菜”后,再点“甜”,列表只显示川菜里的甜口菜,但用户可能想看所有甜口菜——这就需要区分“单选”和“多选”逻辑。
这套源码的解法是:用两个独立状态变量 selectedCuisine 和 selectedTaste,并设置“清空”机制。
WXML 中:
<!-- 菜系标签 -->
<view
wx:for="{{category.cuisine}}"
wx:key="value"
class="tag {{item.value === selectedCuisine ? 'tag--active' : ''}}"
bindtap="onCuisineTap"
data-value="{{item.value}}"
>
{{item.text}}
</view>
<!-- 口味标签 -->
<view
wx:for="{{category.taste}}"
wx:key="value"
class="tag {{item.value === selectedTaste ? 'tag--active' : ''}}"
bindtap="onTasteTap"
data-value="{{item.value}}"
>
{{item.text}}
</view>
JS 中:
data: {
category: require('../../utils/category'),
selectedCuisine: '',
selectedTaste: ''
},
onCuisineTap(e) {
const value = e.currentTarget.dataset.value
// 点击已选中的,视为清空
if (this.data.selectedCuisine === value) {
this.setData({ selectedCuisine: '' })
} else {
this.setData({ selectedCuisine: value })
}
this.navigateToFilterPage()
},
onTasteTap(e) {
const value = e.currentTarget.dataset.value
if (this.data.selectedTaste === value) {
this.setData({ selectedTaste: '' })
} else {
this.setData({ selectedTaste: value })
}
this.navigateToFilterPage()
},
navigateToFilterPage() {
const { selectedCuisine, selectedTaste } = this.data
let url = '/pages/list/list'
if (selectedCuisine) url += `?type=cuisine&value=${selectedCuisine}`
if (selectedTaste) {
url += selectedCuisine ? '&type=taste&value=' : '?type=taste&value='
url += selectedTaste
}
wx.navigateTo({ url })
}
关键点在于 navigateToFilterPage() 函数:它动态拼接 URL 参数,支持三种组合——只选菜系、只选口味、两者都选。而且点击已选标签会清空该维度,避免用户困惑。我实测过,连续点击“川菜”→“辣”→“川菜”,最终 URL 是 /pages/list/list?type=taste&value=la,完美符合预期。
3.3 详情页交互:如何让步骤图和用料清单真正“活”起来?
详情页(pages/content/content)的交互设计,聚焦两个高频痛点:步骤图加载慢、用料清单排版乱。
步骤图优化:GIF + 懒加载 + 占位图。
WXML 中:
<view wx:for="{{steps}}" wx:key="index" class="step">
<image
src="{{item.image}}"
mode="aspectFill"
class="step__image"
bindload="onImageLoad"
binderror="onImageError"
/>
<text class="step__desc">{{item.desc}}</text>
</view>
JS 中:
data: {
steps: [],
loadedImages: [] // 记录已加载成功的图片索引
},
onImageLoad(e) {
const index = e.target.dataset.index
this.data.loadedImages.push(index)
this.setData({ loadedImages: this.data.loadedImages })
},
onImageError(e) {
console.warn('步骤图加载失败:', e.detail)
// 失败时显示占位图
const index = e.target.dataset.index
const steps = this.data.steps
steps[index].image = '/res/img/placeholder.jpg'
this.setData({ steps })
}
bindload 和 binderror 事件确保你能感知每张图的状态;loadedImages 数组用来控制 wx:if 显示加载动画(比如 <view wx:if="{{!loadedImages.includes(index)}}" class="loading">加载中...</view>),但源码没加,因为 GIF 本身有加载反馈。
用料清单排版:CSS Grid 实现等宽自适应。
pages/content/content.wxss 中:
.ingredients {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120rpx, 1fr));
gap: 10rpx;
padding: 20rpx;
}
.ingredient {
background: #fff9f5;
border-radius: 8rpx;
padding: 12rpx;
text-align: center;
font-size: 24rpx;
}
.ingredient__name { font-weight: bold; color: #333; }
.ingredient__amount { color: #999; font-size: 22rpx; }
grid-template-columns: repeat(auto-fill, minmax(120rpx, 1fr)) 是精髓:在宽度足够时,每行显示尽可能多的卡片(最小宽度 120rpx),宽度不足时自动换行,且每张卡片等宽。效果是:iPhone SE 上一行显示2个,iPhone 14 Pro 上一行显示4个,不用写媒体查询。
3.4 页面跳转与参数传递:为什么 wx.navigateTo 要慎用对象?
新手最爱写 wx.navigateTo({ url: '/pages/content/content?recipe=' + JSON.stringify(recipe) }),结果真机上 options.recipe 是 undefined。这是因为 URL 参数长度有限(约2048字符),且特殊字符(如中文、空格)需 encodeURIComponent 编码,极易出错。
这套源码的解法是:只传 ID,数据查表。
所有跳转都用 id:
// 列表页跳转
goToDetail(e) {
const id = e.currentTarget.dataset.id
wx.navigateTo({ url: `/pages/content/content?id=${id}` })
}
// 详情页 onLoad
onLoad(options) {
const id = parseInt(options.id)
const contentData = require('../../pages/content/data')
const recipe = contentData[id]
this.setData({ recipe })
}
parseInt 确保 id 是数字类型,避免字符串 '1' 和数字 1 匹配失败。contentData 是对象,用 id 当 key 查找,O(1) 时间复杂度,比遍历数组快得多。
如果你非要传复杂对象,源码在 utils/storage.js 里提供了安全方案:
// 存入临时缓存
wx.setStorageSync('tempRecipe', recipe)
// 跳转时只传标识符
wx.navigateTo({ url: '/pages/content/content?cacheKey=tempRecipe' })
// 详情页读取
const cacheKey = options.cacheKey
const recipe = wx.getStorageSync(cacheKey)
wx.removeStorageSync(cacheKey) // 用完即删,防内存泄漏
wx.setStorageSync 是同步方法,无回调,适合小数据;removeStorageSync 防止缓存堆积。我试过存 5MB 对象,真机上会卡顿,所以源码只用于极端情况。
4. 实操部署与避坑指南:从导入到真机测试的全流程
4.1 开发者工具导入四步法:为什么必须检查 app.json?
很多人解压后双击 project.config.json 就以为完事了,结果编译报错。正确流程是:
- 打开微信开发者工具 → 新建项目 → 选择源码根目录(不是
UQeWiZpPRinjmzA3f3BU-master-3d8ecd8a63fe1243e2e68d4f64d590a791549bff子目录,而是解压后的顶层文件夹); - AppID 选“测试号”(不要填自己的,避免配置问题);
-
导入后,第一时间打开
app.json,确认pages数组包含所有页面路径:
json { "pages": [ "pages/index/index", "pages/discovery/discovery", "pages/list/list", "pages/content/content", "pages/search/search", "pages/result/result" ], "tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "首页" }, { "pagePath": "pages/discovery/discovery", "text": "发现" } ] } }
如果漏了pages/result/result,搜索后点菜谱会白屏——因为没注册的页面无法跳转。 -
检查
project.config.json中的miniprogramRoot,确保指向.(当前目录),而不是子目录。我见过有人解压后多了一层文件夹,miniprogramRoot指向错误,导致app.js找不到。
提示:导入后若看到“未找到 app.json”,说明你选错了根目录。关闭项目,重新选顶层文件夹。
4.2 真机调试三大雷区及解决方案
雷区一:图片不显示(真机上全是灰色方块)
原因:路径写错或资源未正确放入 res/ 目录。
排查步骤:
- 在开发者工具中,右键图片 → “在资源管理器中打开”,确认路径是 /res/img/xxx.jpg;
- 真机调试时,打开“调试器” → “Network” 标签,看图片请求是否 404;
- 检查 res/ 目录是否在项目根目录下,且 res/img/ 里确实有对应文件(注意大小写,iOS 对大小写敏感);
- 终极方案:把 src="/res/img/xxx.jpg" 改成 src="https://example.com/res/img/xxx.jpg" 测试,如果能显示,证明是本地路径问题。
雷区二:搜索无结果,控制台报 Cannot read property 'includes' of undefined
原因:pages/search/data.js 导入的数据为空数组,或 allRecipes 变量名不一致。
解决方案:
- 在 pages/search/search.js 的 search 函数开头加 console.log('allRecipes:', allRecipes);
- 确认 require('../../pages/list/data') 返回的是数组,不是 undefined;
- 检查 pages/list/data.js 是否有 module.exports = [...],而非 export default [...](小程序不支持 ES6 export)。
雷区三:页面跳转后返回,首页轮播图停止自动播放
原因:<swiper> 组件的 autoplay 属性在页面隐藏时暂停,返回时不自动恢复。
修复方法:在 pages/index/index.js 中添加生命周期钩子:
onShow() {
// 页面显示时重置轮播
this.setData({ swiperCurrent: 0 })
},
onHide() {
// 页面隐藏时记录当前页
this.setData({ swiperCurrent: this.data.current })
}
并在 WXML 中绑定 current 属性:<swiper current="{{swiperCurrent}}">。
4.3 二次开发扩展清单:收藏、评论、API 接入的最小改动路径
这套源码的扩展性,体现在三个“最小侵入点”:
① 加收藏功能(不改页面结构):
- 在 pages/content/content.wxml 的标题下方加按钮:
xml <button bindtap="toggleFavorite" class="btn-favorite"> {{isFavorited ? '已收藏' : '收藏'}} </button>
- 在 pages/content/content.js 中:
```javascript
data: {
isFavorited: false
},
onLoad(options) {
// …原有逻辑
const id = parseInt(options.id)
const favorited = wx.getStorageSync(‘favoritedIds’) || []
this.setData({ isFavorited: favorited.includes(id) })
},
toggleFavorite() {
const id = this.data.recipe.id
const favorited = wx.getStorageSync(‘favoritedIds’) || []
const index = favorited.indexOf(id)
if (index > -1) {
favorited.splice(index, 1)
this.setData({ isFavorited: false })
} else {
favorited.push(id)
this.setData({ isFavorited: true })
}
wx.setStorageSync(‘favoritedIds’, favorited)
}
`` 收藏状态存在本地,无需后端,favoritedIds` 数组存 ID 列表。
② 加评论入口(复用现有页面):
- 在 pages/content/content.wxml 底部加:
xml <view bindtap="goToComments" class="comments-entry"> <text>查看 {{recipe.commentsCount || 0}} 条评论</text> </view>
- 新建 pages/comments/comments 页面,复用 pages/list/list 的结构,只改数据源为 utils/comments.js,列表渲染评论项。
③ 接入真实 API(替换数据层):
- 修改 pages/list/list.js 的 onLoad:
javascript onLoad(options) { wx.showLoading({ title: '加载中' }) wx.request({ url: 'https://api.yourdomain.com/recipes', data: { type: options.type, value: options.value }, success: (res) => { this.setData({ listData: res.data }) }, fail: () => { wx.showToast({ title: '加载失败', icon: 'none' }) }, complete: () => wx.hideLoading() }) }
把 require('../../pages/list/data') 替换为 wx.request,其他逻辑(WXML 渲染、跳转)完全不动。
注意:接入 API 前,务必在微信公众平台后台配置
request合法域名,且服务器需支持 HTTPS。本地调试可用ngrok代理,但正式上线必须备案。
5. 常见问题速查表与独家调试技巧
| 问题现象 | 可能原因 | 快速定位方法 | 解决方案 |
|---|---|---|---|
| 首页轮播图不自动播放 | autoplay 属性未设为 true,或 interval 太大 | 检查 pages/index/index.wxml 中 <swiper> 标签属性 | 添加 autoplay="true" interval="3000" |
| 点击菜谱无反应,控制台无报错 | bindtap 事件名拼写错误,或 JS 中未定义对应函数 | 在 pages/list/list.wxml 中检查 bindtap="goToDetail",再查 pages/list/list.js 是否有 goToDetail 函数 | 确保函数名完全一致,注意大小写 |
| 搜索结果页空白,但控制台无错误 | pages/result/result.js 中 onLoad 未正确解析 options.q | 在 onLoad 开头加 console.log('options:', options) | 使用 options.q || options.q === '' ? options.q : '' 安全获取 |
| GIF 图片在真机上显示为静态图 | GIF 文件损坏,或小程序基础库版本过低(< 2.10.0) | 在开发者工具中右键 GIF → “在资源管理器中打开”,用浏览器打开确认是否动图 | 更新基础库,或用在线工具重新压缩 GIF |
| 分类筛选后,列表页显示“加载中”但一直不消失 | setData 调用位置错误,或数据过滤逻辑返回空数组 | 在 pages/list/list.js 的 filterData 函数结尾加 console.log('filtered:', result) | 确保 result 是数组,且 this.setData({ listData: result }) 在 filterData 后调用 |
独家调试技巧:
-
“断点注入法”查数据流向: 在
pages/list/list.js的onLoad函数第一行加debugger,然后在开发者工具调试器中单步执行,观察options、allRecipes、filteredList的值变化,比console.log更直观。 -
“样式隔离测试”快速定位 CSS 冲突: 如果某个按钮样式异常,临时在
pages/list/list.wxss中加button { background: red !important; },如果变红,证明样式被覆盖;再逐步删掉app.wxss中的全局样式,找到冲突源。 -
“真机日志导出”抓取隐藏错误: 真机调试时,打开开发者工具 → “调试器” → “Console”,点击右上角“导出日志”,保存为
.log文件。用文本编辑器搜索error或warn,常能发现wx:for中wx:key缺失等警告。 -
“页面栈快照”诊断跳转问题: 在任意页面 JS 中调用
console.log(getCurrentPages()),输出当前页面栈数组。如果发现栈里有多个pages/index/index,说明wx.redirectTo误用成了wx.navigateTo,导致页面堆积。
最后分享一个小技巧:每次改完代码,别急着点编译,先看右下角“项目设置”里的“基础库版本”。这套源码适配 2.7.0+,如果你用 2.15.0 版本,bindconfirm 可能不生效——降级到 2.12.0 或升级开发者工具即可。我习惯把基础库版本写在 README.md 里,避免团队成员踩坑。
这套菜谱源码,我用了五年,从最初的 3 个页面,迭代到现在 6 个页面、2 个工具模块、3 类资源目录。它没用任何黑科技,靠的就是对小程序底层机制的尊重:页面注册要严谨,数据流动要清晰,样式管理要分层,资源引用要规范。你跑通它的那一刻,不是完成了一个 demo,而是拿到了一把打开小程序开发大门的钥匙——接下来,门后是什么,由你决定。
简介:这套微信小程序菜谱源码开箱就能用,不用改配置直接导入开发者工具就能运行。首页、发现页、分类列表页、菜谱详情页、搜索页和结果页都已写好,页面之间跳转正常,路径在app.里全注册好了。支持按菜系或口味分类浏览,也能输关键词搜菜名,每道菜带图、步骤、用料说明。代码是原生写法,用标准app.js/app./app.wxss三件套,utils里有常用工具函数,res目录放了图片、GIF等资源,临时资源也打包好了。菜谱数据放在pages/content和pages/list里,用本地模拟方式加载,没依赖后端,适合练手页面渲染、setData绑定、wx:for列表循环、事件传参这些基础操作。想加用户收藏、评论或者接入真实API,结构清晰容易扩展,新手学完能上手做美食类小程序。
227



被折叠的 条评论
为什么被折叠?



