微信小程序商城demo源码+截图:核心页面与全局逻辑详解 简介这是一份面向微信小程序初学者与前端开发者的实战型商城Demo源码资源聚焦基础功能实现与项目结构认知适用于课程设计、快速入门练习及小型电商类小程序原型搭建。压缩包共57个文件包含12个样式文件wxss、11个逻辑脚本js、10个视图模板wxml、5个配置文件json以及15张界面截图png辅以字体与图标资源完整呈现小程序的页面组织、数据绑定、导航跳转与简单交互逻辑。资源包仅95KB轻量易导入目录结构清晰涵盖app.js/app.json/app.wxss等全局配置及pages/、utils/、static/等标准模块划分便于理解小程序工程化规范。目前已有5994人学习下载读者可直接运行调试、对照截图理解UI布局、分析JS逻辑与WXML数据渲染关系并基于此扩展商品列表、购物车或用户登录等常见功能。 小程序商城这套东西网上能找到的demo多到数不清但真正能直接跑起来、代码结构又看得明白的说实在话没几个。我这次整理的“微信小程序商城demo源代码截图”定位就是一个标准的、可二次开发的商城底子把商品展示、分类筛选、购物车、下单流程、个人中心这些核心闭环都串通了。代码里面保留了必要的注释截图配了关键页面的实际效果你可以直接扫码预览也可以下载源码对着页面逐行看逻辑。这套demo适合谁我大概分了这么三类人刚入门小程序开发的学生或转行者需要一个完整的、有业务逻辑的项目来理解页面生命周期、组件通信和网络请求而不是只会写静态页面。接外包或者做课程设计的开发者需要一个能快速改造成交的商城壳子把商品接口、支付参数一换就能当首版交付。产品经理或创业者想用一个小程序demo快速验证商城类业务模式或者拿截图和可运行demo去和团队、投资人沟通需求。这篇文章不只是丢个代码地址给你我会把demo里面几个关键设计讲清楚同时把微信小程序开发里几个高频抱怨——比如顶部导航栏高度怎么适配、request请求怎么封装不重复写代码、分包异步化到底解决什么问题、web-view嵌套H5怎么调起手机扫码、页面怎么控制不让截屏——这些都结合商城场景拆开揉碎。每一条都是我实际跑demo、改demo、踩坑之后总结出来的希望能让你少走点弯路。1. 商城Demo的页面拆解从首页到订单的六个核心页面一个商城demo页面多少并不重要重要的是页面之间能不能形成完整的业务闭环。我拆解一下这套demo里的核心页面以及每个页面在搭建时的设计思路。1.1 首页和商品列表的内容组织首页是用户进入小程序后的第一印象这个页面的核心不是炫技而是用最小的开发成本把“逛”的体验做出来。我的做法是自上而下拆分模块搜索入口暂时没有做搜索页的话可以直接用navigator跳转到商品列表页并带上关键词参数。轮播bannerswiper组件autoplay开启circular循环indicator-dots用自定义圆点。商城首屏banner高度建议按750x360的宽高比切图这样在大多数机型上展示都协调。金刚区快捷入口用flex布局排列图标一般是四到五个跳转到对应分类。这里数据可以写死也可以从接口拉。推荐商品流商品卡片列表展示主图、名称、价格、销量。这里我特意用了scroll-view配合分页加载而不是直接用页面原生的滚动原因很简单商品流后面要接“下拉刷新”和“触底加载”用scroll-view的bindscrolltolower事件处理起来最直接。商品列表接口返回的数据结构我建议统一成这种格式方便前端解析{ code: 0, data: { list: [ { goodsId: 1001, cover: https://xxx.com/a.jpg, title: 商品标题, price: 99.00, sales: 20 }, // 更多商品... ], page: 1, hasMore: true }, msg: success }首页截图里你会看到我在页面底部留了一个“加载中”的状态这个细节很多初学者会忽略但实际用户感知非常强。1.2 分类页、购物车和我的页面怎么搭分类页用的是经典左右联动布局左侧是二级分类列表右侧是对应的商品列表或三级分类。核心实现思路是左侧用scroll-view竖向排列分类名称点击某一项时改变当前分类id。右侧也用scroll-view根据左侧的分类id加载不同的商品数据。右侧滚动时通过bindscroll事件计算当前滚动位置反推应该高亮左侧哪个分类左侧点击时右侧用scroll-into-view定位到对应点位。这里有个容易踩坑的地方scroll-into-view的值对应的是子元素的id而id不能以数字开头所以构建列表时要在分类id前拼一个字符串前缀比如cat- categoryId。购物车页面是这个demo里交互最密集的页面我用了原生checkbox-group和checkbox来管理选中状态。每个购物车条目包含选中框、商品图、商品名、规格描述、单价、数量步进器stepper、小计金额。底部固定一个结算栏左侧全选checkbox右侧显示“合计xx元”和“结算(n)”按钮。数据逻辑上购物车用一个数组维护每个条目包含checked字段。全选操作遍历数组把所有checked置为true或false单条切换时重新计算是否全选。合计金额只在选中的商品里累加注意保留两位小数避免浮点计算带来的显示问题。我的页面需要处理两种状态未登录时显示“点击登录”的占位头像和按钮已登录后显示微信头像、昵称、订单入口、收货地址、优惠券等列表。这个demo里我用wx.getUserProfile获取用户信息再加上wx.login获取code换取openid这样能跑通最基本的登录链路。1.3 商品详情、登录和订单列表的交互细节商品详情页是转化率的关键demo里我做了三块核心区域顶部轮播图展示商品多图swiper组件可复用首页的写法。价格和标题区展示现价、原价划线、标题、销量。这里的价格字段建议前端用分为单位传输避免浮点误差显示时再除以100。SKU选择区demo里做了一个轻量版的规格选择用popup弹层展示规格组例如颜色、尺码点击后选中对应规格并更新价格库存。完整版SKU会涉及规格组合矩阵但demo做到这个程度已经足够演示闭环。登录页要处理好授权逻辑。现在微信对wx.getUserProfile的调用时机有限制必须用户在页面内主动点击触发不能在onLoad里直接弹。我的写法是用户点击登录按钮后才调wx.getUserProfile拿到头像昵称后再把code和用户信息一并提交到后端接口后端解密换取openid并返回自定义登录态token。订单列表页我做了三个tab全部、待付款、待收货。通过segment组件切换状态列表数据根据status参数请求。订单卡片包含订单号、商品缩略图、商品名、实付金额、订单状态按钮去付款/确认收货。空数据时展示一个简单的空状态插画和“暂无订单”文案。有一点值得提醒订单列表的分页处理不能直接复用商品列表的分页逻辑因为订单状态可能变化比如用户取消订单、确认收货切换tab时要重置列表并重新请求否则会出现旧数据残留。2. 全局逻辑和请求层app.js、request封装与导航栏适配很多人写小程序demo页面和组件一大堆但app.js里只有几行默认代码request请求每次都重新写一遍wx.request这是最典型的“能跑但没法维护”的写法。这个demo里我把全局逻辑做了统一这里讲一下设计思路。2.1 app.js里到底该放什么app.js是全局逻辑入口但不要把业务代码全堆在里面。我的做法是App({ globalData: { token: , userInfo: null, systemInfo: null, statusBarHeight: 20, navBarHeight: 44, cartCount: 0 }, onLaunch() { // 1. 获取系统信息用于导航栏适配 const systemInfo wx.getSystemInfoSync() this.globalData.systemInfo systemInfo this.globalData.statusBarHeight systemInfo.statusBarHeight // 2. 读取本地缓存恢复登录态 const token wx.getStorageSync(token) const userInfo wx.getStorageSync(userInfo) if (token) { this.globalData.token token this.globalData.userInfo userInfo } // 3. 静默登录无token时调用wx.login换code if (!this.globalData.token) { this.handleSilentLogin() } }, handleSilentLogin() { wx.login({ success: (res) { // 将 res.code 发送到后端后端换取 openid 并返回 token } }) } })把系统信息缓存到globalData而不是每次用wx.getSystemInfoSync去取是为了性能——这个API在部分机型上会有几十毫秒的耗时全局存一份自定义导航栏组件里直接读取就行。登录态恢复放在onLaunch里同步处理保证页面onLoad的时候能直接拿到app.globalData.token去请求接口。2.2 一劳永逸的request封装商城项目里所有接口都走同一个HTTPS域名所以在请求层做统一处理非常有必要。utils/request.js我封成了Promise风格核心逻辑如下const BASE_URL https://api.example.com/v1 function request(path, method GET, data {}) { return new Promise((resolve, reject) { const token getApp().globalData.token || wx.getStorageSync(token) wx.request({ url: ${BASE_URL}${path}, method: method, data: data, header: { Content-Type: application/json, Authorization: token ? Bearer ${token} : }, timeout: 10000, success: (res) { // 统一处理业务码 if (res.data.code 0) { resolve(res.data.data) } else if (res.data.code 401) { // token过期清理缓存并跳转登录 wx.removeStorageSync(token) wx.navigateTo({ url: /pages/login/login }) reject(new Error(登录已过期)) } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }) reject(new Error(res.data.msg)) } }, fail: (err) { wx.showToast({ title: 网络异常请检查网络, icon: none }) reject(err) } }) }) } module.exports { request }这个封装把三件重复的事收敛了自动带上Authorization请求头不用每个页面手动传token。统一处理HTTP成功、业务码、异常三层的返回值。网络错误和业务错误统一弹toast页面里只关注resolve的数据。调用方写起来非常简洁import { request } from ../../utils/request Page({ async onLoad() { const goodsList await request(/goods/list, GET, { page: 1 }) this.setData({ goodsList }) } })用async/await替代回调嵌套页面代码可读性高了一大截。这里要特别说明小程序基础库版本较老的环境可能不支持async/await项目里建议开启enhance编译增强或者用regeneratorRuntime垫片否则会在低版本微信上报错。2.3 顶部导航栏高度适配看着简单实际很烦“微信小程序顶部导航栏高度”能成为搜索热词说明这个问题确实困扰了不少人。原因很简单微信小程序的胶囊按钮右上角三个点位置由微信统一控制不同机型、不同系统下高度和间距都不一样如果你要用自定义导航栏比如在首页放一个延伸到状态栏的背景图就必须精确计算导航栏高度。我自定义导航栏时是这样计算的const systemInfo wx.getSystemInfoSync() const menuButtonInfo wx.getMenuButtonBoundingClientRect() // 胶囊底部到状态栏底部的距离 const navBarHeight (menuButtonInfo.top - systemInfo.statusBarHeight) * 2 menuButtonInfo.height this.setData({ statusBarHeight: systemInfo.statusBarHeight, navBarHeight: navBarHeight })公式的逻辑是胶囊按钮垂直居中于导航栏所以导航栏高度 胶囊上方距状态栏距离的两倍 胶囊自身高度。把statusBarHeight和navBarHeight传给自定义导航栏组件用padding-top撑开状态栏用height定义导航栏内容区就能实现所有机型一致的导航栏样式。具体到demo里我会在页面顶部放一个占位view高度为statusBarHeight然后下面才是自定义的导航栏内容。首页的轮播图可以直接延伸到导航栏背后形成沉浸式效果。截图里你会看到首页顶部是通栏的banner图就是这个实现方式。3. 热词背后的硬骨头分包、webview扫码、禁止截屏等进阶细节搜“微信小程序”的人多说明大量开发者都卡在了一些看似基础但实际很考验经验的关节点。这一章我挑几个和商城demo关系最密切、同时也是搜索热词里出现频率最高的硬骨头来分析。3.1 分包异步化主包瘦身与跨分包调用商城的核心问题在于页面多、依赖多、图标多如果全部塞进主包很容易就超过2MB限制。微信提供的主包大小限制是1.5MB这在有大量页面和图片的商城项目里几乎必然超限。分包是最直接的解决方案。在app.json里配置{ pages: [ pages/index/index, pages/category/category, pages/cart/cart, pages/mine/mine ], subPackages: [ { root: pages/goods, pages: [ detail/detail, list/list ] }, { root: pages/order, pages: [ list/list, confirm/confirm ] } ] }主包只放底部tabBar对应的四个页面和共用组件商品详情、订单确认这类低频页面通通丢进分包。这样主包体积能控制在800KB左右加载速度提升非常明显。分包异步化是基础库2.20.2以上才支持的特性解决的是分包之间的代码互相引用问题。比如商品详情页在pages/goods分包里但需要引用主包components/price-tag组件传统做法是把这个组件放进分包内部但现在可以用require.async动态加载// 在分包页面里引用另一个分包的模块 async loadSubModule() { const tool await require.async(../../packageA/utils/tool.js) tool.doSomething() }还有占位组件component-placeholder可以用在页面JSON里当某个分包组件还没加载完成时先渲染一个占位元素让页面不会白屏。商城场景里如果你把用户头像组件或自定义弹窗放在公共组件里而业务页面在分包里就很值得用这个方案。3.2 原生web-view加载H5如何优雅调起手机扫码搜索热词里有一条“微信小程序原生中webview加载vue2调用手机扫码优雅实现”这是个非常具体的业务需求。很多商城项目里有H5活动页、运营页面这些页面用web-view内嵌到小程序里但当H5页面需要调起摄像头扫码时就会遇到问题——H5页面里无法直接调起微信的原生扫码wx.scanCode。我的做法是走小程序桥接H5发消息给小程序小程序调起wx.scanCode回调结果再传回H5。流程如下H5页面在需要扫码时调用wx.miniProgram.postMessage// H5页面vue2项目 import wx from weixin-js-sdk document.getElementById(scanBtn).addEventListener(click, function () { wx.miniProgram.postMessage({ data: { type: scanCode } }) wx.miniProgram.navigateBack({ delta: 1 }) // 可选返回小程序页面 })小程序端web-view组件监听bindmessage事件web-view src{{h5Url}} bindmessageonWebviewMessage/web-viewPage({ onWebviewMessage(e) { const data e.detail.data[0] if (data data.type scanCode) { wx.scanCode({ success: (res) { // 把扫码结果通过URL参数传回H5或者用webview的src更新来传递 const resultUrl ${this.data.h5Url}?scanResult${encodeURIComponent(res.result)} this.setData({ h5Url: resultUrl }) } }) } } })这里有几个关键点bindmessage不是立即触发的H5端postMessage后只有在特定时机比如小程序后退、分享、组件销毁小程序才会收到。最可靠的触发方式是让H5在发完消息后主动wx.miniProgram.navigateBack退回到小程序页面但这样体验有点割裂。另一种方案是web-view页面不离开而是让小程序端通过别的途径感知——不过实测下来用更新web-view的src来传参是最简单可控的。H5端引入的weixin-js-sdk需要用npm安装并且无需通过wx.config注入配置只要在微信内置浏览器环境下wx.miniProgram相关API就能用。如果H5直接调用JSSDK里的wx.scanQRCode需要公众号的JS接口安全域名配置和wx.config签名在小程序web-view里往往会因为域名校验问题失败。所以“优雅实现”的答案本质上是把扫码能力下沉到小程序原生层而不是强行在H5里做。3.3 控制不让截屏隐私保护和系统能力边界“微信小程序控制不让截屏”也是一个高频需求尤其是商城里的订单详情页、优惠券页或者金融类产品的交易页面。我的经验是尽量做但别指望完全禁掉微信目前也没有提供100%禁止用户截屏的API。目前有两个可用的能力wx.setVisualEffectOnCapture在iOS录屏或截图时把页面内容替换为隐私保护效果wx.setVisualEffectOnCapture({ visualEffect: hidden, success: () { console.log(开启截屏保护) } })这个API只对安卓部分机型生效iOS上效果有限而且仅在页面处于前台时有用。页面水印后台提醒在敏感页面上叠加半透明水印水印内容携带用户手机号或用户ID这样即使被截图也能追溯到是谁泄露的。这算成本最低且最实际的防截屏方式。所以在demo里我在订单确认页和我的页面里加了水印层同时在需要防截屏的页面onShow里调一次wx.setVisualEffectOnCapture文档注释里也写了边界说明——不要承诺用户“能完全禁止截屏”。3.4 单选框、checkbox在商城里的正确用法搜索词里有“微信小程序单选框”我猜是因为很多教程只讲了radio怎么用但没讲什么时候应该用radio、什么时候用checkbox更没讲怎么自定义样式。在商城场景里单选框radio-group / radio适合“单选”场景比如选择收货地址、选择支付方式、选择优惠券。一次只能选一个。复选框checkbox-group / checkbox适合“多选”场景比如购物车里选择多个商品结算、订单筛选里的多状态勾选。demo里的购物车用checkbox地址选择用radio。这里分享一个自定义样式的技巧微信自带的radio和checkbox默认样式很丑想改成圆形色块或品牌色需要app.wxss里覆盖/* 自定义checkbox样式 */ .checkbox-custom { width: 40rpx; height: 40rpx; border: 2rpx solid #ccc; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; } .checkbox-custom-active { background-color: #e93b3d; border-color: #e93b3d; }如果要让点击整个商品卡片也能切换选中状态可以给卡片绑定bindtap事件在事件里手动翻转checked值而不是依赖checkbox自己的点击区域。demo里的购物车就是这样处理的这样有效点击区域大很多交互更顺手。4. 源码目录与调试心得从看懂到改到自己的项目里最后来个实操向的总结。拿到一套源码怎么快速了解它、运行它、改造成自己的项目这是很多人问的问题。4.1 先把目录结构看明白一套规范的小程序源码app.js、app.json、app.wxss是入口然后pages目录下按业务模块分文件夹。我的商城demo目录长这样├── app.js ├── app.json ├── app.wxss ├── project.config.json ├── sitemap.json ├── assets/ # 静态资源图片、图标 │ ├── icons/ │ └── images/ ├── components/ # 公共组件 │ ├── nav-bar/ # 自定义导航栏 │ ├── good-card/ # 商品卡片 │ ├── stepper/ # 步进器 │ ├── popup/ # 弹层 │ └── empty/ # 空状态 ├── pages/ # 主包页面 │ ├── index/ # 首页 │ ├── category/ # 分类 │ ├── cart/ # 购物车 │ └── mine/ # 我的 ├── pages-goods/ # 商品分包detail/list ├── pages-order/ # 订单分包confirm/list └── utils/ # 工具库 ├── request.js # 网络请求封装 ├── auth.js # 登录态管理 └── util.js # 格式化等初次拿到源码按这个顺序看app.json看全局配置和页面注册 →app.js看全局数据 →utils/request.js看接口请求方式 → 再从首页index.js进去看页面逻辑。截图我会放在screenshots/目录下对应每个页面一张方便对照。4.2 运行前需要先搞定的几个问题拿到demo后直接导入开发者工具通常跑不起来原因基本是下面几个AppID问题开发者工具导入时要用自己的小程序AppID注册个个人小程序即可。如果暂时不想注册选择“测试号”也能跑大部分功能但wx.login和真机预览会受限。合法域名问题demo里接口地址是示例域名本地调试时需要在开发者工具右上角“详情-本地设置”里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”。否则wx.request会被挡掉。后端接口demo内置了mock数据模式请求失败时会读取本地模拟数据保证前端流程能走通。如果你想对接真实后端在utils/request.js里把BASE_URL换成自己的域名并按后端返回格式调整code判断即可。基础库版本至少选择2.30.0以上否则分包异步化和wx.setVisualEffectOnCapture这些API可能不存在。开发者工具里可以切换基础库版本建议用最新稳定版。图片资源项目里的商品图都是示例链接可能因图片服务器限制导致加载失败真机预览前最好替换成自己的CDN图片地址否则首屏会显得很空。4.3 调试技巧怎么有效查看网络请求、排查白屏很多朋友会搜“微信小程序抓包”其实在小程序自己的调试工具里看网络请求已经非常方便不需要额外抓包工具。打开开发者工具切到Network面板可以清晰看到每个请求的URL、请求头、响应体。真机上也可以用真机调试-调试器查看同样的面板。排查白屏问题的顺序我总结了几个关键点看console有没有报错。大部分白屏是JS运行时出错导致页面数据没渲染出来或者组件报错中断了渲染。把报错信息复制到搜索引擎基本能定位到原因是API调用参数错误、变量为undefined等。看网络请求是否成功。如果页面依赖接口数据接口返回失败或超时页面就停留在初始空状态看起来像是白屏。用Network面板确认请求有没有发出、返回状态码是什么。检查分包是否加载成功。分包页面首次加载时需要下载分包代码如果网络波动或分包配置错误可能长时间白屏。真机上打开调试模式能看到分包加载日志。检查是否有setData过大的警告。商城商品列表如果一次性加载太多数据setData会非常慢导致页面卡在白屏。建议每次分页加载20条以内图片用懒加载。还有一条容易被忽略如果使用uni-app这类跨端框架生成的小程序在微信开发者工具里白屏但手机上预览却正常这种情况多半是开发者工具基础库版本和框架编译后的代码不兼容。解决方案是把基础库切到最新版关闭“ES6转ES5”还不行就在HBuilderX或CLI里重新打包上传不要改代码。5. 最后分享几个自己做商城demo的判断这套demo我从零搭起来前后也折腾了不少晚上最后结合自己的使用经验和踩过的坑给你几个建议第一不要一上来就写自定义组件。很多新手喜欢把什么都封装成组件以为这样代码优雅结果业务逻辑改来改去组件通信把自己绕晕。我的建议是把功能跑通页面直接写等确实有两三个页面复用同一块UI时再抽组件。这个demo里的商品卡片、导航栏、步进器是抽了组件的因为它们确实在多个页面复用但首页的banner、金刚区就没有硬抽。第二商品图片和banner的比例一定要提前想清楚。商城页面最影响美观的就是图片比例不统一。我的经验是商品图统一750x750banner图统一750x360上传前先用工具批量裁剪不要在代码里拉伸适配否则不同屏幕尺寸下会出现变形。第三登录态和token过期是一定要处理的。商城用户逛着逛着token过期了请求返回401这时候如果什么都不做用户会以为页面坏了。我的request封装里已经处理了401自动跳登录你改代码时不要把这个逻辑删掉否则用户加到购物车一点“结算”就闪退体验非常差。第四向后扩展的方向可以想清楚再动手。这个demo目前没有接支付没有做优惠券和秒杀但如果你的业务目标是完整的商城闭环下一步可以优先加这几块微信支付wx.requestPayment、优惠券中心列表领券、秒杀倒计时setInterval实现、客服会话wx.openCustomerServiceChat。这四块都是商城类微信小程序的标准配置也都不难做完之后整套demo的完整度会再上一个台阶。最后再唠叨一句代码这东西看十遍不如跑一遍。demo里的注释我已经把每个文件、每个方法的作用都标清楚了截图也能帮你快速对齐页面效果但真正理解这套商城逻辑一定要动手改一个页面试试。把商品卡片的样式换成你自己的品牌色把首页banner换成真实活动的图把购物车结算金额的计算逻辑跟着走一遍整个项目的门道基本就摸透了。本文还有配套的精品资源点击获取