尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微信小程序点餐源码拆解与仿写:从页面结构到购物车状态

微信小程序点餐源码拆解与仿写:从页面结构到购物车状态 简介麦当劳点餐微信小程序页面源码是一份面向微信小程序开发者和初学者的完整示例工程围绕点餐场景展示了从页面搭建、样式设计到核心交互的整体实现方式。压缩包内共收录80个文件大小仅约1.06MB核心包括WXML页面结构、WXSS样式、JavaScript脚本和JSON配置等代码文件另有27张图片素材及txt、docx、md等说明文档目录层次清楚能帮助使用者快速定位到页面、逻辑或配置的对应模块也便于直接导入微信开发者工具进行预览。目前已有150人学习浏览。借助该源码读者可以理解小程序页面布局、数据绑定、组件调用、分类筛选、商品详情、购物车管理以及订单提交等常见功能的编码思路也能参考前后端数据交互和微信支付接入的初步设计。无论用于课程设计、个人练手还是作为点餐类商业项目的起步原型都能获得直观帮助具备较强的参考价值。1. 麦当劳点餐微信小程序页面源码到底该读什么“麦当劳点餐的微信小程序页面源码.zip”这名字在资源站一抓一大把。刚接触微信小程序的人最喜欢下这种包打开一看几百个文件app.js 里全是 wx.login 和 request从此收藏夹吃灰。实际上这类点餐源码的教学价值不在“麦当劳”三个字而在“点餐”背后的那套工程套路菜单列表怎么按分类联动购物车角标、总价怎么由局部状态驱动页面和页面之间怎么传参这份 zip 适合三类人刚学会 WXML 语法、想找一个业务闭环练手的前端新人给餐饮商家做私域点餐、自提/外送小程序的开发者以及想理解小程序页面模块怎么拆分、路由怎么注册的人。读这份源码之前先给自己立个规矩不照抄页面只抽离结构和数据流。毕竟商标素材用了商用会有麻烦学到套路就行。2. 先拆源码包目录结构、路由与启动页2.1 用 app.json 反推页面框架拿到压缩包先解压我一般不看 README第一步打开 app.json。它是整个小程序的入口清单pages 数组里列了所有页面路径数组顺序直接决定冷启动时先加载哪个页面。常见做法是{ pages: [ pages/index/index, pages/menu/menu, pages/cart/cart, pages/order/order ], tabBar: { color: #666666, selectedColor: #333333, list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/menu/menu, text: 点餐 } ] }, window: { navigationBarTitleText: 麦当劳点餐, navigationBarBackgroundColor: #f7f7f7 } }这段 JSON 是理解源码的钥匙。pages 数组的每一项都对应页面目录下的四个同名文件.js、.json、.wxml、.wxss少一个就算注册了也编译不过。站在源码分析的角度你要做的第一件事不是看代码而是把 pages 数组里的路径抄下来对照项目目录给每个页面写一句职责说明index 是品牌展示和爆品入口menu 是核心点餐页cart 是购物车详情order 是订单确认。这样整个小程序的地图就摊开了。如果把 window.navigationBarTitleText 改成其它文字再编译能看到导航栏标题的变化。“修改刚进入的加载页面”其实指的就是这里pages 数组的第一项既是首页也是冷启动最先渲染的页面想换成菜单页做主入口把 pages 里的顺序对调即可不需要动业务代码。tabBar 里最多配五个页面配置了 tabBar 的页面不能使用 wx.navigateTo 跳转只能用 wx.switchTab这是很多新手读源码时看不懂跳转为啥不生效的原因。2.2 页面组件拆分一个点餐页最少拆几层麦当劳这种点餐页业务上至少要分三层分类侧栏、商品列表、购物车浮层。下面这个目录树是我仿写时的最小分层比源码包的复杂结构更适合做参考miniprogram/ ├── app.js ├── app.json ├── app.wxss ├── pages/ │ ├── index/ │ ├── menu/ │ │ ├── menu.js │ │ ├── menu.json │ │ ├── menu.wxml │ │ └── menu.wxss │ ├── cart/ │ └── order/ ├── components/ │ ├── goods-card/ │ ├── cart-bar/ │ └── spec-popup/ └── utils/ ├── request.js └── mock.js每个页面目录里js/json/wxml/wxss 四个文件的职责必须分清。json 只做页面级配置比如usingComponents引入组件、navigationBarTitleText单独设置标题wxml 只写结构不要在里面写业务逻辑wxss 负责样式但商品卡片和购物车这种会被多个页面复用的视图要抽到 components 里。注意自定义组件和页面一样目录里也必须有四个同名文件否则 usingComponents 会直接报 “Component is not found”。组件划分还有个实操标准一个 wxml 文件超过 300 行或者同一段模板在两个页面里各出现一次就值得拆成组件。goods-card 负责展示商品图、名称、价格和加购按钮cart-bar 负责固定在底部的“去结算”栏spec-popup 负责弹窗选择规格。这三个组件在点餐源码里几乎是固定组合。如果发现源码包里的 components 目录是空的说明这份包把业务全写在了页面上只能自己动手重构。路径职责关键依赖pages/index首页、品牌展示swiper、goods-cardpages/menu分类联动点餐scroll-view、spec-popup、cart-barpages/cart购物车明细与数量增减goods-card、cart-barpages/order自提/外送信息与支付form、request2.3 启动页与顶部导航栏高度适配点餐小程序的启动页顺序和导航栏视觉是“第一张脸”。pages 数组第一项决定冷启动展示但要注意冷启动时要做登录态检查常见做法是单独设一个 splash 页面放在第一位检查完成后用 wx.reLaunch 跳去 menu而不是直接把登录逻辑塞进 App.onLaunch。后者会导致页面加载时序不可控。顶部导航栏的高度不能写死。不同机型胶囊按钮位置不同微信官方没有公开统一高度我一般用菜单按钮位置反推const menuRect wx.getMenuButtonBoundingClientRect(); const navHeight menuRect.top menuRect.height 8;menuRect 拿到胶囊按钮的 top、height、right用 top 加上 height 得导航栏下边界再加安全间距就是自定义导航的总高。很多仿写源码里把导航栏高度硬编码成 44px在 iPhone 全面屏上就会顶到状态栏里。“修改刚进入的加载页面”除了调 app.json 的 window 配置还可以用页面级 json 覆盖但自定义导航时必须在 json 里设置navigationStyle: custom再配合上面这段计算代码才能保证适配所有机型。需要额外说一句如果你打开源码包看到的是 pages.json 而不是 app.json说明这份代码是用 uni-app 写的编译目标是微信小程序。uni-app 的页面路由配置集中在 pages.json同时它要求项目由 hbuilderx 或 cli 编译后再导入开发者工具。判断原生与 uni-app 的方法很简单原生必有 app.jsuni-app 项目根目录是 src 且没有 app.js。两层结构差很多后面所有抓包和组件调试方式也会跟着变。3. 仿写点餐主流程菜单列表、分类联动与购物车状态3.1 菜单数据建模成“分类 商品 规格”三层点餐页面最忌讳把数据拍平。麦当劳点餐的数据模型应该是三层分类数组汉堡、小食、甜品、饮品、每个分类下的商品数组、商品下的规格与价格。这样设计是为了让前端在切换分类时只需要告诉视图“当前分类的索引”商品列表按该分类的 goods 渲染不用做复杂的二次过滤。const mockMenu [ { id: c1, name: 汉堡, goods: [ { id: g1001, name: 巨无霸, desc: 双层牛肉饼经典款, price: 210, // 单位是分避免浮点误差 img: /assets/burger.png, specs: [ { name: 单点, price: 210 }, { name: 套餐, price: 350 } ] } ] } ];价格字段单位用“分”而不用“元”是电商类小程序源码里的常见约定。浮点数做累加会出现 0.1 0.2 0.30000000000000004购物车一旦多次加购总价就对不上。在数据模型层面就用整数分渲染时再转字符串能省掉一整套精度处理。商品 id 要保证全局唯一不要用分类 id 加序号拼接因为后续购物车、订单、支付都要拿这个 id 做关联。购物车的数据结构要提前想好维护成数组还是对象。我一般用对象key 是商品 id值是{ count, spec }。这样加购、减购、查购物车都是 O(1) 操作。数组虽然天然适合列表渲染但要找一个商品得遍历商品多了容易卡。3.2 用 scroll-view 做分类侧滑与商品区联动点餐页面的交互核心是双栏联动左侧分类栏是短 scroll-view右侧商品列表是另一个 scroll-view。两个区域独立滚动分类点击后右侧自动定位到对应商品区。view classmenu scroll-view classmenu-cats scroll-y view wx:for{{cats}} wx:keyid classcat-item {{activeCat index ? active : }} >data: { cart: {}, totalCount: 0, totalPrice: 0 }, onAdd(e) { const goods e.detail.goods; const key goods.id : goods.spec; const cart { ...this.data.cart }; // 浅拷贝避免直接改 data 引用 if (cart[key]) { cart[key].count 1; } else { cart[key] { ...goods, count: 1 }; } this.setData({ cart }); this.recalc(); }, recalc() { let count 0; let price 0; Object.keys(this.data.cart).forEach((key) { const item this.data.cart[key]; count item.count; price item.count * item.price; }); this.setData({ totalCount: count, totalPrice: price }); }onAdd 接收子组件冒泡上来的事件e.detail 里带商品信息。key 用商品id:规格名拼接能区分同一个商品的不同规格这是点餐类小程序购物车最容易出现的 bug一个汉堡单点和套餐是两种价格如果只按商品 id 做 key规格切换时库存和价格都会被污染。recalc 的调用时机只有一个购物车数据真正发生变化时。不要在 wx:for 里调用 recalc也不要在每次 setData 后马上调用第二次那样会白白多一次setData渲染。总价和数量只是购物车状态的一个投影投影值要统一从this.data.cart算出来不要自己随着 add/sub 累加。因为减购、清空、撤销操作都会修改 cart如果总价靠累加维护某个分支漏写了价格就会错。这也符合“单一数据源”的原则cart 是唯一真相总计永远是派生值。3.4 规格弹窗为什么用 radio 而不是 picker点餐时“单点/套餐”这种规格选择用弹窗加 radio 更合适。picker 的滚轮选择适合日期、省市区这种列表规格选项通常只有两三个而且需要同时展示每个规格的价格差异radio 的单选语义更清晰。微信小程序的 radio 需要和 radio-group 搭配radio-group bindchangeonSpecChange label wx:for{{goods.specs}} wx:keyname radio value{{item.name}} checked{{specIndex index}} color#2b2b2b / text{{item.name}} ¥{{item.price / 100}}/text /label /radio-group注意 radio 的默认样式在不同平台上差异很大尤其是 Android 上会出现样式错位。如果源码里出现 radio先检查是否给 radio-group 设置了display: flex或flex-direction: column。在 iOS 的渲染机制里radio 原生组件层级高于普通 view弹窗里出现点不到的情况要给 radio-group 加position: relative; z-index: 10。这也是“微信小程序单选框”在点餐源码里最常被讨论的体验细节。4. 把页面源码接到真实数据请求封装、Mock 与登录态4.1 写一个支持 Promise 的 request 封装大部分点餐页面源码里的请求都是直接 wx.request 裸调回调地狱和数据解析逻辑堆在一起。我一般会先写一个 utils/request.js把 method、header、超时、错误码判断统一收敛function request(url, { method GET, data {}, header {} } {}) { return new Promise((resolve, reject) { wx.request({ url, method, data, header: { content-type: application/json, Authorization: wx.getStorageSync(token) || , ...header }, timeout: 10000, success(res) { if (res.statusCode 200 res.statusCode 300) { resolve(res.data); } else { reject(new Error(HTTP ${res.statusCode})); } }, fail(err) { reject(err); } }); }); } module.exports { request };这段代码解决四个问题所有请求自动带 token统一超时把 wx.request 的 success/fail 转成 Promise可按需扩展 header。注意 wx.request 的 data 在 GET 请求里会被拼到 query 上在 POST 里会进 body。content-type 默认是 application/json如果后端要求表单格式要改成application/x-www-form-urlencoded并在 header 里手动指定。Promise 化之后页面里的调用就干净很多const menu await request(/api/menu); this.setData({ cats: menu.categories });不要忘了在页面文件顶部 require 这个模块。真机预览时如果发现请求一直 pending先确认开发者工具里“不校验合法域名”的开关是否打开以及 request 的 url 是不是以 https 开头。微信小程序对域名有强校验本地开发可以用http://127.0.0.1配合“不校验”开关但体验版和正式版都必须用已备案的 https 域名并在 mp 后台配到 request 合法域名里。4.2 用 Mock 数据先跑通页面不依赖后端点餐页面源码经常带一个 mock.js因为前端想先看到效果后端还在写接口。Mock 数据可以做成和真实接口同构的函数保证后面换真实接口改动最小function getMenu() { return new Promise((resolve) { setTimeout(() { resolve({ code: 0, data: mockMenu }); }, 300); }); }mock.js 和 request.js 的导出函数签名保持一致页面里统一 import。这里故意用 setTimeout 模拟网络延迟而不是直接 resolve是为了让加载态逻辑也能被验证。页面里的骨架屏不会因为“瞬间返回”而一闪而过开发体验更接近真实。切换到真实接口时只需要把页面顶部 import mock 改为 import request或者在 mock.js 里做一个开关const USE_MOCK false; // 改成 true 就走本地假数据实际项目中我更喜欢保留一个全局开关因为后面联调阶段经常要两边切换特别是在后端接口没写完又不想耽误 UI 走查的时候。4.3 登录态用 code 换 token 的完整流程点餐小程序要拿到用户手机号、生成订单就必须有登录态。微信小程序的登录链路是 wx.login 拿到临时 code传给自己的后端后端再拿 code 去微信接口换 openid 和 session_key然后签发自己的 token。常见错误是把 code 当成 token 直接存起来这是不对的code 五分钟就失效而且只有后端具备调微信接口的权限。wx.login({ success: async (res) { const { code } res; const { token } await request(/api/login, { method: POST, data: { code } }); wx.setStorageSync(token, token); this.setData({ logged: true }); } });较新版本的基础库也支持wx.login({ timeout })设置超时但我更建议把 wx.login 包一层 Promise在用户体验上做串行处理先登录成功再去请求菜单。很多页面源码把 wx.login 放在 App.onLaunch而菜单请求在 Page.onLoad 里同时发出token 还没回来菜单请求就 401 了。处理方式一是在 request 里做 401 拦截后自动重放二是让登录 Promise 先于业务请求。新人阶段我更推荐后者逻辑更直白。订单提交接口的参数设计有两个容易踩坑的点一是地址信息要先用 chooseAddress 或表单拿到 recipient/phone/address再在 submit 时一并传给后端二是金额不要让前端传最终总价正确做法是前端传商品 id 列表和规格后端按照菜单价格重算金额防止用户篡改。页面源码如果已经用了“后端重算”的思路这份源码的工程质量就比较高。4.4 用抓包和 vConsole 定位接口问题页面源码联调阶段一定会遇到“数据没显示、接口报 500、参数格式不对”这类问题。在开发者工具里打开调试器切到 Network 面板能看到每个 wx.request 的请求头、响应值和耗时。这个面板就是最简单的“微信小程序抓包”入口不用额外装工具。真机上要抓包先确认小程序是否开启了调试模式因为正式版小程序默认不走系统代理抓包工具看不到流量。另一个更省事的办法是引入 vConsole。在微信开发者工具里不生效但真机预览时小程序页面右下角会出现一个绿色的 vConsole 按钮点开就能看 console、网络请求和 system 信息。如果源码包没有自带 vConsole可以在 app.js 顶部require(./utils/vconsole.min.js)然后new VConsole()改动量很小。真机上网络请求失败的报错往往比开发者工具更真实很多 Android 的证书校验问题就只能在真机上暴露。注意开发者工具 Network 面板里看不到的数据不一定代表请求失败。先看 url 有没有被自动拼接成http://再看 header 是否带上了 Authorization最后看响应体是不是 JSON。多数点餐页面源码里有一个res.code的字段只有 code 为 0 时数据才有效记住这个约束能少踩很多空数据渲染的坑。5. 仿写完成后要检查的 5 个细节5.1 冷启动白屏与路由注册遗漏改了 app.json 后模拟器冷启动如果出现白屏先看 Console 里有没有 “Page not found” 或 pages 数组路径与文件不符的报错。常见错误是把目录名写错比如pages/menu在磁盘上叫pages/menus。另一种情况是新增页面没有在 app.json 里注册点击跳转时直接报“页面不存在”。每次新增页面第一步永远是同步 app.json 的 pages 数组。5.2 rpx 与 px 混用的布局漂移源码里如果同时出现 rpx 和 px往往会在不同宽度的机型上出现布局漂移。rpx 是微信小程序的响应式单位按 750 设计稿换算px 是物理像素在 iPhone 和 Android 上宽度表现不同。一个页面里如果导航栏高度用 px商品卡片间距用 rpx在全面屏机型上就会看到明显错位。规范做法是与设备宽度相关的间距统一用 rpx像素级边框和阴影细线用 px自定义导航的高度用 wx.getMenuButtonBoundingClientRect 计算结果这是唯一需要动态计算的场景。商品卡片价格文字被截断通常是外层容器宽高用了 rpx而字体大小用了 px 导致内部空间不足。5.3 setData 频繁更新导致的掉帧菜单列表里如果每次点击商品都直接把整棵菜单树重新 setData页面滚动会明显掉帧。setData 的代价和传输数据量成正比点餐页面里购物车数据更新时不需要把菜单列表也带上。优化方法是把购物车数据和菜单列表拆成两个 data 字段只更新受影响的字段。如果需要更新数组中的某一个元素用setData({ [cart. key .count]: count })这种路径写法只更新变化的节点。5.4 图片缓存与本地附件路径商品图是远程 URL 时需要做域名校验而源码包里的图片如果是以 base64 或本地相对路径存在就要注意体积。一张商品图转成 base64 后体积会膨胀三分之一大量内嵌会让页面加载明显变慢。如果想把远程图片缓存到本地可以结合 wx.env.user_data_path 这个文件系统根路径把下载成功的图片写入wx.env.USER_DATA_PATH下下次直接读本地文件减少网络请求。不过对于点餐页面来说图片素材变化频率低这种缓存方案收益不大只有商品图数量特别多时才值得做。5.5 体验版二维码才是最终验收页面源码在自己电脑的开发者工具上跑得通不代表真机上也行。最后的验证落在体验版上在开发者工具右上角上传代码填好版本号和备注到 mp 后台设成体验版生成二维码。手机上扫码后重点测三件事冷启动加载顺序是否正确、真机网络请求是否带上了合法域名、导航栏在刘海屏上有没有遮挡。这三个问题开发者工具里几乎测不出来只能真机见真章。验完这个版本这份源码才算真正被你消化了。本文还有配套的精品资源点击获取
返回列表