简介:简易手机商城微信小程序页面模板源码,适合中小商家、前端初学者或需要快速上线商城业务的开发者,可在微信内搭建具备浏览、选购、结算能力的线上店铺。资源包含163个文件,压缩包约1.02MB,主要类型涵盖PNG界面素材、JSON配置、WXSS样式、JS逻辑与WXML页面结构,覆盖商品分类、商品列表、商品详情、购物车、结算及个人中心等核心模块,并预留地址管理、订单等扩展接口,便于直接替换数据与后端API后投入使用。页面基于WeUI风格设计,交互逻辑清晰,代码注释与目录结构较为规整,适合学习小程序生命周期、组件通信及分包配置等知识点。该模板累计已有67人学习下载,对希望借助微信生态开展线上销售但又缺乏原生开发经验的团队而言,是一个低成本、可快速改造的实用起点。
1. 手机商城小程序源码包,先别急着导入
拿到一个手机商城的微信小程序页面模板源码zip包时,我通常不会直接拖进微信开发者工具看效果,而是先解压看目录。这个包里除了常见页面,还有goodsDetail.js、sharecode.js、cart.js、person.js、test.js、classify.js、goodsList.js、myAddr.js、app.js这样一批文件,基本覆盖了商城从首页到支付的完整链路。对于想快速上线一个商城小程序、又不想从零写页面的团队来说,这套模板能省掉大量排版和交互工作;但它的价值不在双击运行,而在于理解“小程序页面模板+业务逻辑”到底是怎么粘合的。如果你正被微信小程序的项目结构、页面传参和接口对接绕晕,这篇拆解能让你少走弯路。
2. 模板工程结构与数据流:从app.js到页面路由
2.1 解压后先看这几类文件
传统网页模板拿过来是 HTML/CSS/JS,小程序模板则是 WXML/WXSS/JS/JSON 四件套的结构。这份源码的命名很有规律:goodsList.js对应商品列表页,goodsDetail.js对应详情页,再加上cart.js、classify.js、person.js、myAddr.js,基本一眼能看出页面职责。值得注意的反而是sharecode.js和test.js,这两个文件在简单模板里很少见,属于分销逻辑和调试辅助,后面会单独说。
| 文件 | 职责 | 与商城业务的关系 |
|---|---|---|
| app.js | 全局 App 实例,启动时初始化登录态、请求封装、全局数据 | 所有页面刷新的起点 |
| goodsList.js | 商品列表页,分页加载、筛选、搜索入口 | 商城首页和类目页共用 |
| goodsDetail.js | 商品详情页,规格切换、加购、分享链接生成 | 成交转化核心 |
| cart.js | 购物车页,本地缓存、数量增减、总价计算 | 结算前的数据仓库 |
| classify.js | 商品分类页,一级/二级分类联动 | 帮用户快速定位商品 |
| person.js | 个人中心,订单入口、地址入口、设置 | 用户信息承载 |
| myAddr.js | 收货地址管理,对接微信收货地址 API | 订单履约前置 |
| sharecode.js | 分享码生成与解析,带推广人参数 | 分销/拼团场景 |
| test.js | 测试页,常用来放 mock 数据或接口自测 | 联调前验证渲染 |
刚拿到压缩包时,我一般会先删除test.js对应的页面注册,或者把它保留成独立分包,避免它干扰正常页面编译。微信开发者工具对未注册的页面会报错,所以如果发现提示页面路径不存在,先检查app.json的pages数组里是不是残留了pages/test/test。
2.2 app.js 中的全局状态和请求封装
模板里app.js通常做三件事:初始化全局变量、在onLaunch里拉取登录态、封装wx.request。下面这段是比较通用的写法,我在多个商城模板里都见过类似结构:
// app.js App({ globalData: { token: '', userInfo: null, baseURL: 'https://api.example.com' }, onLaunch: function () { // 启动时先读本地 token,避免每次冷启动都重登 const token = wx.getStorageSync('token') if (token) { this.globalData.token = token } // 拿到 token 再去换取用户信息 this.login() }, login: function () { const that = this // wx.login 返回临时 code,不能直接当身份凭证 wx.login({ success: res => { const code = res.code wx.request({ url: that.globalData.baseURL + '/login', method: 'POST', data: { code: code }, success: r => { that.globalData.token = r.data.token wx.setStorageSync('token', r.data.token) } }) } }) } })这段代码有几个关键点。code有效期只有 5 分钟,必须立刻通过后端换成自己的token;换 token 后要同步写入globalData和本地缓存,这样页面重新打开时能直接读取。很多模板会把baseURL写死在各个页面里,后面改环境就很痛苦,建议统一收敛到app.globalData,页面里用const app = getApp()去取。
2.3 页面路由与参数传递
商城典型页面栈是首页 -> 列表 -> 详情 -> 购物车 -> 结算,模板里路由通过app.json的pages数组注册,跳转用wx.navigateTo。比如goodsList跳到goodsDetail时,会带goodsId和来源参数:
// goodsList.js 跳转详情页 goDetail: function (e) { const id = e.currentTarget.dataset.id wx.navigateTo({ url: '/pages/goodsDetail/goodsDetail?goodsId=' + id + '&from=list' }) }// goodsDetail.js 接收参数 Page({ onLoad: function (options) { // options.goodsId 是商品 id,options.from 标记来源 this.setData({ goodsId: options.goodsId, from: options.from || '' }) this.fetchDetail(options.goodsId) } })这里有个容易忽略的坑:wx.navigateTo的url长度有限制,如果拼接的参数里有超长字符串,会被截断。常见的解法是把数据先存到globalData或wx.setStorageSync,跳转时只传索引或 id。模板里如果出现详情页商品显示不全,先看看是不是参数在跳转时被截断了。
3. 核心页面模块代码级拆解:列表、详情与购物车
3.1 首页与商品列表:分页加载与响应式适配
首页通常由 banner 和商品列表组成,goodsList.js承担了列表加载的核心逻辑。前端页面模板为了适配不同屏幕宽度,WXML 里一般用rpx做响应式适配,不用写媒体查询。商品卡片在这种场景下最常用的布局是两列瀑布流,配合scroll-view或onReachBottom触底加载。
// goodsList.js 分页加载 Page({ data: { list: [], page: 1, pageSize: 10, loading: false }, onLoad: function () { this.fetchGoods() }, fetchGoods: function () { if (this.data.loading) return this.setData({ loading: true }) const app = getApp() wx.request({ url: app.globalData.baseURL + '/goods', data: { page: this.data.page, pageSize: this.data.pageSize }, success: res => { this.setData({ list: this.data.list.concat(res.data.list), page: this.data.page + 1 }) }, complete: () => { this.setData({ loading: false }) } }) }, onReachBottom: function () { // 触底后继续拉下一页 this.fetchGoods() } })这段代码里loading状态很关键。如果触底事件触发频率过高,loading能拦截重复请求;concat保留上一页数据,不做全量替换,否则滑动体验会断层。下拉刷新则要配合onPullDownRefresh,在回调里把page重置成 1,并清空list。注意page的语义在真实接口里可能从 0 开始,前后端必须约定好。
3.2 商品详情页与 sharecode.js 的分享码逻辑
详情页是全模板里业务密度最高的地方。goodsDetail.js除了要加载商品图、价格、库存,还要处理规格切换和“加入购物车”。这部分常见的实现是:规格数组存在skuList里,点击规格项时动态过滤出可选组合,避免用户选到无货组合。
// goodsDetail.js 规格选中状态处理 selectSku: function (e) { const { keyIndex, valueIndex } = e.currentTarget.dataset const selected = this.data.selectedSku selected[keyIndex] = valueIndex this.setData({ selectedSku: selected }) // 根据当前所选规格,从 skuList 中匹配价格和库存 const matched = this.data.skuList.find(item => { return item.specIds.join(',') === selected.join(',') }) if (matched) { this.setData({ currentPrice: matched.price, currentStock: matched.stock }) } }sharecode.js在模板里比较特殊,它的职责不是渲染页面,而是生成带推广人参数的分享路径。我见过不少模板会把推广人 id 直接拼到path里,像pages/index/index?inviter=123。在goodsDetail.js里,分享逻辑通常长这样:
// goodsDetail.js 转发给好友 onShareAppMessage: function () { const shareInfo = sharecode.encode({ goodsId: this.data.goodsId, inviter: wx.getStorageSync('userId') }) return { title: this.data.goodsName, path: '/pages/goodsDetail/goodsDetail?share=' + shareInfo, imageUrl: this.data.shareImage } }这里提醒一句:sharecode.js的编码算法如果是简单 base64,很容易被改参数薅羊毛。如果要用于真实分销体系,至少要在后端做签名校验,前端只负责生成和展示。
3.3 购物车与结算页的状态管理
cart.js一般不会把购物车数据打到后端,而是用wx.setStorageSync做本地持久化。这样用户把商品加入购物车后,即使杀掉小程序,数据也能通过本地缓存恢复。模板里商品数量增减的核心逻辑如下:
// cart.js 修改数量 changeQty: function (e) { const index = e.currentTarget.dataset.index const delta = e.currentTarget.dataset.delta // 1 或 -1 const list = this.data.cartList let qty = list[index].qty + delta // 数量不能小于 1,不能超过库存 if (qty < 1) { qty = 1 wx.showToast({ title: '至少购买一件', icon: 'none' }) return } if (qty > list[index].stock) { qty = list[index].stock wx.showToast({ title: '库存不足', icon: 'none' }) return } list[index].qty = qty this.setData({ cartList: list }) this.saveCart(list) this.updateTotal() }注意模板里qty和stock都可能来自接口,前端只做乐观更新,后端结算时仍需重新校验库存。updateTotal通常用reduce累加选中商品的价格,这里要注意浮点精度问题,常见做法是把分转成元时用Math.round处理,避免出现0.1 + 0.2的经典误差。
首页、列表、详情、购物车这一整套页面串下来,其实就构成了一个最小可用的商城闭环。我拿到模板后会先走一遍这个闭环,确认商品能加入购物车、数量能改、总价能算出来,再考虑接后端。
4. 模板的接口对接与后端逻辑补齐:从 test.js 到真实服务器
4.1 用 test.js 做本地 mock,把渲染先跑通
test.js在模板里的存在感很低,但它的价值在于联调。我在没有后端接口时会先把test.js里的 mock 数据改成目标接口的返回结构,然后在列表页临时把url指向 mock 数据函数,等真实接口就绪再切回去。
// test.js mock 数据 const mockGoods = [ { id: 1, name: '示例商品', price: 1999, // 单位是分 stock: 100, image: '/assets/good.png' } ] function getMockGoods() { return new Promise(resolve => { setTimeout(() => { resolve({ list: mockGoods }) }, 300) }) } module.exports = { getMockGoods }这种做法的好处是页面渲染不需要网络请求,前端页面模板可以独立验证布局和交互。等真实接口给出后,只需要替换fetchGoods里的url和data结构。注意 mock 数据的字段命名要和后端最终字段保持一致,否则后面改起来要动很多setData的位置。
4.2 登录、收货地址与支付对接
商城模板绕不开三个关键接口:登录、收货地址、支付。登录在app.js里已经用了wx.login换 token,这里的坑在于后端拿 code 换 openid 时,需要配置小程序的 AppSecret,且请求微信接口需要用 HTTPS。模板不会包含这些真实配置,都需要自己补。
myAddr.js对接的微信能力是wx.chooseAddress,它返回的收货地址是微信用户在自己账户里保存的。需要注意:每次调用都会弹窗让用户授权,如果用户之前拒绝过,需要在模板里引导到设置页重新打开授权,否则success回调永远不进。
支付则要调用wx.requestPayment,下单和签名必须在后端完成,前端只负责调起支付。模板里一般会预留一个payOrder方法:
// 支付下单示例 payOrder: function (orderId) { const app = getApp() wx.request({ url: app.globalData.baseURL + '/order/pay', method: 'POST', data: { orderId: orderId }, success: res => { // res.data 是后端用小程序支付密钥签好的支付参数 wx.requestPayment({ timeStamp: res.data.timeStamp, nonceStr: res.data.nonceStr, package: res.data.package, signType: 'RSA', paySign: res.data.paySign, success: () => { wx.showToast({ title: '支付成功' }) } }) } }) }这里最容易踩的坑是package字段,它和 JS 的关键字同名,在 WXML 里如果直接绑定会出问题,但在 JS 里作为对象属性没问题。另外signType老版本模板可能是MD5,新微信支付要求用RSA,对接时按商户平台的协议为准。
4.3 接口联调时的抓包和调试技巧
联调阶段我最常用的是微信开发者工具自带的 Network 面板,它能直接看到wx.request的请求头、参数和返回状态。遇到线上环境问题,再用 Charles 抓包手机端微信小程序的 HTTPS 流量。需要注意的是,在 Charles 里要做 SSL Proxying 配置,否则看到的全是加密乱码。工具里开启“不校验合法域名”可以让你在本地环境访问http://localhost或内网 IP,但真机预览时这个开关失效,所以测试真机环境必须把接口域名加入白名单。
提示:不要为了省事直接关闭域名校验就提审,微信审核时会检查实际请求的域名是否在
request合法域名列表里,没有备案或未配置的域名会被打回。
调试时还可以在控制台执行wx.getStorageSync('token')看看登录态是否写入成功,很多页面报 401 都是 token 没拿到或过期引起的。
5. 把模板跑起来:AppID 配置、域名校验与常见报错处理
5.1 导入项目时先改 AppID
打开微信开发者工具,选择“导入项目”,目录选到你解压 zip 后的源码根目录。这里最重要的一步是检查project.config.json里的appid,如果没有自己的小程序 AppID,可以先点“测试号”导入,但测试号无法使用支付、订阅消息等能力。建议直接登录微信公众平台创建一个小程序,把 AppID 拷贝到project.config.json的appid字段,再重新编译。
5.2 跳过域名校验的正确姿势
在开发阶段,点击开发者工具右上角“详情” -> “本地设置”,勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。这样就能用http://接口联调,但上线前必须改成 HTTPS 白名单地址。如果是用 HBuilderX 跑uni-app转化的小程序,域名校验位置同理,只是入口在项目的 manifest.json 里。
5.3 常见报错定位
模板跑不起来时,先看控制台报错是哪种类型:
| 报错关键词 | 实际原因 | 处理方向 |
|---|---|---|
app.json: pages未找到 | 页面目录残留 | 对照 zip 内文件路径修正 pages 数组 |
request:fail url not in domain list | 域名白名单缺失或未勾选不校验 | 本地开不校验,上线配白名单 |
wx.login:fail | AppID 无效或网络问题 | 检查 AppID 和工具登录状态 |
component is not found | 引用了不存在的自定义组件 | 看对应 json 文件usingComponents路径 |
我拿到模板后通常会先全局搜索http://把接口地址统一替换成自己的环境变量,再顺手搜索test.js的引用路径,确保没有测试代码泄漏到正式构建里。最后一步是在详情页里点一次“转发”,确认onShareAppMessage返回的path能正常打开页面,避免分销场景下分享链接跳错页面。
本文还有配套的精品资源,点击获取