简介:这是一套面向小程序开发初学者、课程设计与毕业设计学生的商城类小程序源码,围绕商品展示、购物车、订单结算等典型电商场景,帮助读者理解一个可运行商城项目的整体结构与实现思路。压缩包共68个文件,约3.32MB,以30个png与4个jpg、1个jpeg图片资源承担商品与界面素材,9个js、7个wxml、7个wxss与7个json构成页面逻辑、结构、样式及配置主体,另含说明压缩包等辅助内容。已有652人学习下载,说明其在入门练手与课设参考中具有一定认可度。源码按pages、image、js等目录组织,涵盖商品管理、订单处理、支付对接、用户管理与营销活动等模块的代码骨架,读者可据此梳理前后端分工、页面跳转与数据流转方式,并在此基础上替换素材、调整业务逻辑,快速搭建自己的商城小程序原型。
1. 拿到一份小程序商城源码,先别急着点运行
上周有个做前端的朋友发来一个压缩包,说客户临时要一个能演示的商城小程序,三天后就要看效果。他打开目录翻了十分钟,页面文件、组件、云函数、配置文件混在一起,连从哪个入口启动都没摸清。这种场景其实很常见:一份完整的小程序商城源码,价值不在于代码写得多漂亮,而在于它把商品展示、购物车、订单、用户中心这条主链路跑通了,你只需要把它接上自己的数据就能演示。这份资源适合三类人:课程设计要交作业的学生、毕业设计需要一个可运行底座的开发者、以及想快速搭一个商城原型验证想法的人。它用的是小程序原生技术栈,不依赖复杂框架,改起来门槛低,但前提是你得先搞清楚它的目录约定和启动顺序,否则就会像那位朋友一样,对着文件树干瞪眼。
2. 项目定位与技术栈:原生小程序商城到底由哪些部分组成
2.1 为什么这类商城项目大多选原生而不是跨端框架
拿到源码第一件事是看技术选型,因为它决定了你后面改代码的方式。这份商城源码走的是小程序原生路线,页面用 WXML + WXSS + JS 三件套,逻辑层跑在 JS 里,视图层由框架渲染。原生方案的好处是启动快、调试链路短、没有编译中间层,你在开发者工具里改一行样式,模拟器立刻就能看到效果。跨端框架虽然能一套代码多端跑,但多了一层构建,出问题时排查路径变长,对于课程设计或演示型项目来说反而是负担。
原生商城的骨架通常由四块组成:全局配置app.json负责注册页面和窗口样式,app.js管全局生命周期和登录态,app.wxss放公共样式,页面目录按业务拆分。判断一份源码是否完整,看app.json的pages数组就够了——商品列表、商品详情、购物车、订单确认、订单列表、个人中心,这几个页面齐全,主链路基本就通了。如果缺了订单确认页,那购物车到下单这一段就是断的,演示时会卡住。
2.2 目录结构逐层拆解:从入口文件到业务页面
一份典型的商城源码目录大致长这样,不同项目命名略有差异,但分层逻辑一致:
miniprogram-shop/ ├── app.js # 全局逻辑:登录态、购物车缓存初始化 ├── app.json # 页面注册、tabBar、窗口配置 ├── app.wxss # 全局样式变量与公共类 ├── project.config.json # 开发者工具项目配置 ├── pages/ │ ├── index/ # 首页:轮播、分类、商品推荐 │ ├── goods-list/ # 商品列表:分类筛选、分页 │ ├── goods-detail/ # 商品详情:规格选择、加购 │ ├── cart/ # 购物车:数量增减、选中结算 │ ├── order-confirm/ # 订单确认:地址、金额、提交 │ ├── order-list/ # 订单列表:状态筛选 │ └── user/ # 个人中心:登录、订单入口 ├── components/ # 自定义组件:商品卡片、数量步进器 ├── utils/ # 工具函数:请求封装、金额格式化 ├── images/ # 静态图片资源 └── mock/ # 模拟数据(部分项目用云开发则无此目录)这个结构里最容易被忽略的是utils/request.js。商城所有数据交互都走它,里面通常封装了wx.request的 Promise 化、统一错误提示和 token 注入。你后面要接真实后端,改的就是这一个文件,而不是在每个页面里逐个替换请求地址。另一个关键是app.js里的globalData,登录态、用户信息、购物车数量角标一般挂在这里,页面通过getApp()取用。
2.3 数据层设计:本地缓存、模拟数据与真实接口的三种接法
商城类项目的数据来源决定了你能不能快速跑起来。常见有三种接法:第一种是纯本地 mock,商品数据写死在mock/goods.js里,页面直接require进来渲染,优点是零依赖、打开就能看效果,缺点是购物车和订单状态刷新后丢失;第二种是本地缓存加 mock,购物车用wx.setStorageSync持久化,商品仍走 mock,这种最适合演示,因为加购、改数量、结算这条链路能完整走通;第三种是接真实后端,把utils/request.js的 baseURL 指向自己的服务,接口按约定返回。
我一般建议先跑第二种,确认页面跳转和交互没问题,再切第三种。切换时注意一个细节:mock 数据的字段名要和后端接口对齐,比如商品价格字段有的项目叫price,有的叫salePrice,单位是元还是分也要统一。金额用分做整数存储是常见做法,避免浮点误差,展示时再除以 100 格式化。这个坑在订单金额计算时特别容易翻车,后面避坑章节会细说。
3. 跑起来:开发者工具导入、配置与首屏验证
3.1 导入项目前必须检查的三个配置文件
很多人拿到 zip 解压后直接拖进开发者工具,结果报一堆错,问题往往出在配置文件上。导入前先打开project.config.json,确认appid字段。如果源码里写的是作者自己的 appid,你直接导入会提示无权限,解决办法是改成你自己的测试号 appid,或者在工具里选择「不使用云开发」的测试模式。第二个要看的是miniprogramRoot,有些项目把小程序代码放在子目录里,这个字段指向错误会导致工具找不到app.json。第三个是compileType,确认是miniprogram而不是插件类型。
{ "miniprogramRoot": "miniprogram/", "compileType": "miniprogram", "appid": "touristappid", "setting": { "es6": true, "minified": false } }上面这段是导入时最需要关注的字段。miniprogramRoot告诉工具代码根目录在哪,如果源码是扁平结构就填./;appid用touristappid可以免注册直接预览,但云开发和部分接口用不了;es6转 es5 建议开着,老版本基础库兼容性更好。改完保存,重新打开项目,工具会按新配置加载。
3.2 用开发者工具导入并跑通首页的完整步骤
打开开发者工具,选择「导入项目」,目录指向解压后的根目录,appid 填测试号或自己的,后端服务选「不使用云服务」(除非源码明确用了云开发)。导入成功后,工具会自动编译,模拟器里应该出现首页。如果首页白屏,先看控制台的报错:常见的是图片路径 404 或app.json里注册的页面文件不存在。图片 404 不影响逻辑,但页面文件缺失会直接导致跳转失败。
首页能显示后,按主链路走一遍:点商品进详情,选规格加购物车,进购物车改数量,点结算进订单确认,提交后看订单列表。哪一步断了,就去看对应页面的onLoad里请求了什么数据、跳转用了什么路径。路径错误是高频问题,wx.navigateTo的 url 必须以/开头写绝对路径,或者用相对路径但层级要对,写错就是「页面不存在」。
// utils/request.js 常见封装形态 const BASE_URL = 'https://your-api.example.com'; function request(options) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'content-type': 'application/json', // token 从缓存取,登录后写入 'Authorization': wx.getStorageSync('token') || '' }, success(res) { if (res.statusCode === 200 && res.data.code === 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg || '请求失败', icon: 'none' }); reject(res.data); } }, fail(err) { wx.showToast({ title: '网络异常', icon: 'none' }); reject(err); } }); }); } module.exports = { request };这段封装的价值在于把 baseURL、token 注入、错误提示三件事收口到一处。BASE_URL换成你自己的服务地址,code === 0是常见的业务成功约定,不同后端可能用code === 200,按实际改。Authorization头在登录后才有值,所以登录页要把 token 写进缓存,否则后续请求都是匿名的。改完这个文件,全站请求就都指向新后端了,不用逐页改。
3.3 真机预览与基础库版本的两个注意点
模拟器跑通不等于真机没问题。点工具右上角「预览」,扫码在手机上打开,重点看两处:一是底部 tabBar 图标是否正常显示,图标路径在app.json的tabBar.list里配置,尺寸建议 81×81 像素,路径写错在模拟器可能缓存显示、真机直接空白;二是页面滚动和 fixed 定位元素,购物车底部的结算栏在部分机型上会被安全区遮挡,需要在样式里加padding-bottom: env(safe-area-inset-bottom)。
基础库版本也值得留意。app.json里如果没写"lazyCodeLoading": "requiredComponents",页面多的时候首屏会偏慢;而用了较新 API(比如wx.getUserProfile已调整)的页面,在低版本基础库上会直接报错。工具里「详情 → 本地设置 → 调试基础库」可以切换版本,建议选一个覆盖主流机型的版本,别盲目追最新。
4. 核心模块拆解:购物车、订单与登录态的实现细节
4.1 购物车模块:数据结构设计与数量增减逻辑
购物车是商城项目里最能体现数据设计功力的模块。常见的数据结构是一个数组,每项包含商品 id、规格、单价、数量、选中状态:
// 购物车单项结构 { goodsId: 1001, skuId: 'red-xl', // 规格标识,同商品不同规格算不同项 name: '纯棉短袖', price: 8900, // 单位:分 count: 2, checked: true, cover: '/images/goods/1001.jpg' }关键点在于skuId。同一个商品选了红色 XL 和蓝色 M,应该是购物车里的两项,而不是把数量合并。判断是否已存在时用goodsId + skuId组合匹配,匹配到就累加数量,匹配不到就 push 新项。数量增减要卡边界:减到 1 时禁用减号,加到库存上限时提示。选中状态影响结算金额,全选按钮的逻辑是遍历所有项统一置位,而不是简单取反,否则部分选中时点全选会出现反直觉结果。
// 加入购物车:按 goodsId + skuId 去重合并 function addToCart(cart, item) { const idx = cart.findIndex( c => c.goodsId === item.goodsId && c.skuId === item.skuId ); if (idx > -1) { cart[idx].count += item.count; } else { cart.push({ ...item, checked: true }); } wx.setStorageSync('cart', cart); // 持久化,刷新不丢 return cart; }findIndex做组合匹配是这段的核心,checked默认 true 让新加的商品自动参与结算。持久化用同步存储,购物车数据量小,同步写入的开销可以接受,换来的是页面切换时数据不丢。注意count累加后要再校验一次库存上限,别只在 UI 层拦。
4.2 订单确认与金额计算:分单位存储与优惠叠加顺序
订单确认页是整个链路里计算最密集的地方。金额一律用分做整数运算,展示时(price / 100).toFixed(2)。计算顺序通常是:商品小计 → 减优惠券 → 加运费 → 得应付。顺序错了结果就对不上,比如先加运费再减券和先减券再加运费,在满减门槛判断上会有差异。常见做法是先算商品小计,判断是否满足包邮门槛,再应用优惠,最后得出应付金额。
// 订单金额计算:单位统一为分 function calcOrderAmount(items, coupon, freightRule) { const subtotal = items.reduce((sum, i) => sum + i.price * i.count, 0); // 满 99 元包邮,否则收 8 元运费 const freight = subtotal >= 9900 ? 0 : 800; // 优惠券不能超过商品小计 const discount = coupon ? Math.min(coupon.amount, subtotal) : 0; const payable = subtotal - discount + freight; return { subtotal, freight, discount, payable }; }reduce累加小计,Math.min防止优惠券把金额减成负数,这是血泪经验——不加这层保护,测试时用大额券能把应付算成负的。freightRule参数留了扩展位,实际项目里运费规则可能按地区、按重量,这里简化成门槛判断,二次开发时可以替换成配置表驱动。
4.3 登录态与用户信息:token 存储与页面级拦截
小程序的登录态一般这样流转:用户点登录,前端调wx.login拿 code,发给后端换 token,token 存进wx.setStorageSync,后续请求带上。页面级拦截放在app.js的onLaunch或各页面的onShow里判断,没 token 就跳登录页。注意别在onLaunch里做异步跳转,那时页面栈还没准备好,稳妥做法是在需要登录的页面onShow里检查。
// 页面级登录拦截 onShow() { const token = wx.getStorageSync('token'); if (!token) { wx.navigateTo({ url: '/pages/login/login' }); return; } this.loadCart(); }onShow而不是onLoad,是因为从登录页返回时onLoad不会再触发,onShow会,这样登录完回来能继续加载数据。return很关键,拦住后续逻辑,否则没登录也去请求购物车,接口报 401 又是一堆报错。
5. 避坑与排查:导入、跳转、金额、真机四类高频问题
5.1 导入后编译报错「app.json 未找到」
现象是工具打开项目直接红屏,提示找不到app.json。原因多半是project.config.json里的miniprogramRoot指向了错误目录,或者解压时多套了一层文件夹。解决方法是看解压后的实际层级,把miniprogramRoot改成app.json所在目录的相对路径,比如"miniprogram/"或"./"。改完关掉项目重新导入,别在原地反复编译,缓存有时会干扰。
5.2 页面跳转报「页面不存在」
现象是点商品卡片没反应,控制台提示navigateTo:fail page "xxx" is not found。原因有两个:一是app.json的pages数组里没注册目标页面,二是跳转 url 路径写错。先核对pages里有没有这个路径,再检查wx.navigateTo的 url 是否以/开头。tabBar 页面不能用navigateTo跳,要用switchTab,这也是高频翻车点。
5.3 订单金额出现小数误差或负数
现象是结算金额显示88.99999或负数。原因是价格用了浮点元做运算,或者优惠券没做上限保护。解决方法是全链路改用分做整数,展示层再格式化;优惠金额用Math.min(discount, subtotal)兜底。改的时候把所有涉及金额的字段 grep 一遍,别只改订单页,商品详情和购物车的小计也要同步。
5.4 真机上 tabBar 图标空白或底部按钮被遮挡
现象是模拟器正常、真机 tabBar 图标不显示,或购物车结算栏被手机底部横条盖住。图标问题检查app.json里iconPath和selectedIconPath的路径与尺寸,建议 81×81 且用 png。遮挡问题在结算栏样式加padding-bottom: env(safe-area-inset-bottom),并确认页面根容器没有写死高度。这两个问题模拟器都不一定复现,必须真机预览才能发现。
6. 二次开发:把演示项目改成能接真实业务的底座
6.1 替换数据源的三步改造法
第一步改utils/request.js的BASE_URL和成功码约定,让全站请求指向你的服务。第二步对齐字段:把 mock 里的price、cover、stock等字段名和后端返回逐一比对,不一致的在请求层做一层映射,别在页面里到处改。第三步处理分页,列表页通常有page和pageSize参数,mock 阶段是本地切片,接后端后改成透传,并在onReachBottom里做加载更多。三步做完,页面代码基本不用动,这是封装请求层带来的好处。
6.2 用配置表驱动运费与优惠,避免硬编码
演示项目里运费和优惠往往写死在函数里,二次开发时建议抽成配置:
// config/promotion.js module.exports = { freight: { freeThreshold: 9900, // 满 99 元包邮 defaultFee: 800 // 默认运费 8 元 }, coupon: { maxDiscountRatio: 1 // 优惠不超过商品小计 } };计算函数从配置读取阈值和费率,运营调整时改配置不改逻辑。这个习惯在真实项目里能省很多返工,尤其是运费规则按地区变化时,配置表可以扩展成按省份查表。
6.3 验证改造是否成功的检查清单
改完后按这条链路自测:首页加载商品 → 详情选规格加购 → 购物车改数量并勾选 → 结算金额与手算一致 → 提交订单 → 订单列表出现新单 → 退出重进购物车数据还在。每一步都对照接口返回和控制台日志,金额用手算核对一遍。真机再走一遍,重点看安全区和图标。
| 检查项 | 预期结果 | 失败时看哪里 |
|---|---|---|
| 首页商品列表 | 有数据、可滚动 | request.js 的 BASE_URL、接口返回结构 |
| 加购去重 | 同规格合并、不同规格分行 | addToCart 的匹配条件 |
| 结算金额 | 与手算一致、无负数 | calcOrderAmount 的运算顺序 |
| 登录拦截 | 未登录跳登录页 | 页面 onShow 的 token 判断 |
| 真机 tabBar | 图标正常、不被遮挡 | app.json 图标路径、安全区样式 |
这张表我每次接新商城源码都会过一遍,比漫无目的地翻代码高效得多。从那以后我每次拿到一份陌生的小程序源码,都强制先跑通主链路再动一行代码,因为跳过验证直接改,出了问题根本分不清是源码本身的坑还是自己改出来的。希望这份拆解能帮你少走几步弯路,把这份商城源码真正用起来。
本文还有配套的精品资源,点击获取