uni-app菜单页面卡顿主因是图片未懒加载、长列表缺虚拟滚动、规格弹窗无防重,需用scroll-view+enhanced实现滚动,后端返回含specifications和stock的完整菜品数据,前端加防抖锁与本地缓存回写。

菜单页面不是堆几个 uni-list 就能上线的,尤其在微信小程序里——图片懒加载没配、长列表没虚拟滚动、规格弹窗没防重复触发,用户一滑就卡顿,点两次“加入购物车”就生成两笔重复订单。
菜单数据结构必须带规格和库存字段
餐饮菜单不是静态展示,后端返回的每道菜至少得包含:specifications(规格数组)、stock(实时库存)、status(上下架状态)。漏掉 stock 会导致用户加购时才发现“已售罄”,体验断层;没 specifications 就没法支持“微辣/中辣/免葱/加蛋”这类高频操作。
常见错误是前端硬编码规格选项,比如写死 ["小份", "中份", "大份"]。实际菜品可能只有“中份+大份”,或者“单人套餐”根本不走规格逻辑。正确做法是让后端返回完整规格树,前端只做渲染和选中态管理。
- 后端字段示例:
{"id": 101, "name": "毛肚", "price": 48.00, "stock": 23, "specifications": [{"id": 1, "name": "份量", "options": [{"id": 11, "name": "中份", "price": 0}, {"id": 12, "name": "大份", "price": 12}]}, {"id": 2, "name": "辣度", "options": [{"id": 21, "name": "微辣"}, {"id": 22, "name": "中辣"}]}]} - 前端不要用
v-for直接遍历specifications渲染,要用递归组件或uView的u-popup+u-form组合,确保多级规格可嵌套 - 库存变化必须实时响应:下单成功后,要主动调用接口刷新该菜品的
stock,不能等下拉刷新或页面重进
滚动区域必须用 scroll-view 而非 page 默认滚动
微信小程序原生 page 滚动在长菜单页极易触发“页面整体回弹”,导致顶部分类栏错位、吸顶失效。且无法监听滚动位置做懒加载或曝光埋点。
正确做法是用 scroll-view 包裹整个菜单容器,并显式设置 scroll-y 和固定高度(如 height: calc(100vh - 120px)),把滚动控制权收归组件内部。
- 必须加
enhanced属性开启增强型滚动,否则 iOS 上快速滑动会卡顿 - 监听
@scroll事件时,别直接触发uni.showLoading,先用节流(如 Lodash 的throttle或手写 16ms 间隔)防抖 - 图片懒加载不能只靠
lazy-load属性——微信基础库 2.25.2+ 才支持,老版本需配合intersectionObserver自行实现
加入购物车必须带防抖 + 状态锁 + 本地缓存回写
用户手快连点“加入购物车”,后端没做幂等,前端又没拦截,结果同一桌生成 3 条相同订单项,后厨打印出三张单——这是餐饮系统最常被投诉的问题之一。
关键不是“怎么加”,而是“怎么确保只加一次”。uni.setStorageSync 存购物车只是基础,重点在点击瞬间的控制流。
- 按钮绑定
@click="handleAddToCart(item)"时,函数开头立刻设this.isAdding = true,并禁用按钮(:disabled="isAdding") - 加购逻辑执行完(无论成功失败),必须用
setTimeout(() => { this.isAdding = false }, 300)解锁,不能依赖 API 返回——网络超时或 Promise reject 会导致按钮永远灰掉 - 每次加购后,除了更新本地缓存,还要同步触发
uni.$emit('cart-change'),让购物车角标和页面顶部总价实时响应,避免用户以为没点上又狂点 - 不要用
vuex或pinia存购物车主数据——小程序生命周期里 store 实例可能被回收,优先用uni.setStorageSync持久化,store 只做运行时缓存
微信小程序包体积超 2MB 怎么砍菜单资源
菜单页通常是首屏最大资源消耗者:高清菜品图、SVG 图标、字体文件、冗余组件全堆在里面,一不小心就超 2MB 上线红线。
砍体积不是删功能,而是分层加载:首屏只载必要内容,非关键资源异步加载或 CDN 托管。
- 菜品图必须用
webp格式 + 尺寸裁剪(如 375×240px),禁止直接上传原图;用uni.uploadFile上传前先用uni.compressImage压缩 - 所有图标改用 iconfont 或 uni-app 官方
uni-icons,禁用 PNG/SVG 文件引用 - 菜单分类栏(如“热菜”“凉菜”“酒水”)用
uni-segmented-control,但它的样式文件较大,可提取 CSS 到全局app.vue,避免每个页面重复引入 - 如果用了
uView,按需引入组件:在main.js中只注册u-button、u-popup等菜单页真正需要的,别import uView from 'uview-ui'全量引入
菜单看似简单,实则是性能、交互、并发、合规四重关卡。最易被忽略的是“规格选择后未校验库存”——用户选了“大份毛肚”,但此时库存只剩 1 份中份,前端没拦截就允许加购,后端又没做最终扣减校验,结果支付成功却出不了单。这个环节必须前后端双重兜底,不能只信任何一边。











