☰
微信小程序商城开发实战:从云开发到购物车与SKU设计
2026/10/3 18:34:26 网站建设 项目流程

1. 这个毕设到底要做什么:功能清单与工作量估算

先说句实话,微信小程序类的电商项目,在毕业设计里属于"烂大街但就是稳"的类型。选题不算新颖,但它不容易翻车,为什么?因为微信小程序生态成熟,文档齐全,云开发、支付、登录这些能力都有现成接口,哪怕你不会后端,也能用云函数凑出一套完整业务闭环。而我这次要讲的项目,是基于微信小程序的米家商城,也就是模拟米家商城核心购物链路的一个毕业设计。

在做功能清单之前,必须要先给自己泼一盆冷水:毕设不是商业项目,你不要想着把米家商城全部功能搬过来。真实米家商城有智能硬件绑定、场景自动化、社区、售后工单、视频专区……这些你全做,做到答辩那天都做不完。正确的做法是抓住电商系统的主干,也就是"逛 + 选 + 买 + 查"。

我最终敲定的功能范围是这样的:

  • 用户模块:微信一键登录、个人资料展示、收货地址管理
  • 商品模块:首页Banner与金刚区入口、商品分类、商品列表、商品搜索、商品详情
  • 交易模块:SKU选择、加入购物车、购物车编辑、下单结算、订单创建
  • 订单模块:订单列表、订单详情、取消订单、确认收货
  • 我的模块:我的订单、我的钱包(余额展示,非必须)、售后入口(只做页面)

这里我建议每个模块都先列成一张功能表,再给每项标一个工作量等级,比如"登录 = S""SKU = M""退款 = L"。为什么会建议标工作量?因为很多同学做毕设容易陷入"越做越嗨、最后收不住"的状态。你做了两天促销秒杀系统,觉得很酷,但到了答辩前一周发现订单页面还没做,然后就慌了。所以第一步,先把功能边界确定,写在论文需求分析那一章里,后面所有开发都在这个清单内推进。

工作量我用了一个倒推法:毕业设计给到开发的时间一般是三到八周,按每天有效编码3小时来算,分类页我规划了2天,购物车1天半,登录加token体系2天,支付联调4天——因为微信支付和商户号申请本身就是最拖时间的环节,后面我会专门讲怎么用模拟支付降低风险。

2. 小程序架构与米家商城页面地图:tabBar、分包与导航设计

2.1 tabBar的选择:为什么我用四个tab而不是五个

微信小程序的全局配置里,app.json的tabBar是最先要定的。我见过很多商城项目,底部栏放五个入口:"首页、分类、购物车、消息、我的",看着很全,但对毕设来说,消息中心是个很尴尬的模块——你既没有客服系统,也没有推送体系,做出来就是一个空壳页面,答辩时一打开就是"暂无消息",反而暴露功能没做全。

我的方案是四个tab,也就是:

{ "tabBar": { "color": "#999999", "selectedColor": "#FF6B00", "backgroundColor": "#ffffff", "list": [ { "pagePath": "pages/index/index", "text": "首页" }, { "pagePath": "pages/category/category", "text": "分类" }, { "pagePath": "pages/cart/cart", "text": "购物车" }, { "pagePath": "pages/user/user", "text": "我的" } ] } }

选中色我用了米家风格的橙色#FF6B00,这个颜色在视觉上也很有辨识度。四个tab的好处是:每个页面都有明确的核心任务,答辩时老师问"这个tab为什么存在",你能答出它承载的业务价值。比如分类页对应"逛",购物车对应"选",不会答不上来。

tabBar的图标建议用PNG图片,尺寸81px*81px,且必须是纯色可识别的小图标。这里有个坑:微信开发者工具可以直接用iconfont字体图标吗?不建议在tabBar里用,因为tabBar的icon只支持图片路径,不支持字体图标。你可以在页面内用字体图标,但tabBar一定得准备图片资源。我当时从iconfont下载了SVG,再转成PNG,注意选色要匹配selectedColor,否则选中的时候图片颜色对不上。

2.2 分包加载:为什么首页和商品详情必须拆开

米家商城首页信息密度高,图片多,商品列表长。如果不做分包,所有页面打成一个包,主包很容易超过2MB限制。但这里我要补充一个容易忽略的点:微信小程序的包限制分两个维度,一个是主包大小2MB,一个是整个小程序总大小20MB(现在主包上限是2M,分包总大小上限30M,具体以官方文档为准)。对毕设项目来说,老老实实把图片资源放云端,是避免包体积炸掉的根本方案。

我这个项目的分包结构大概是这样的:

├── pages/ // 主包 │ ├── index/ │ ├── category/ │ └── cart/ ├── packageGoods/ // 商品分包 │ ├── pages/goods-list/ │ ├── pages/goods-detail/ │ └── pages/search/ └── packageOrder/ // 订单分包 ├── pages/confirm-order/ ├── pages/order-list/ └── pages/order-detail/

分包配置写在app.json的subPackages字段里,用root指定目录。这里有个实操细节:如果某个页面要从A分包跳转到B分包,微信开发者工具会自动要求你用url带上前缀,比如/packageGoods/pages/goods-detail/goods-detail?id=xxx。一旦分包多了,这种路径字符串很容易写错,我的习惯是单独建一个constants/route.js文件,把页面路径全部导出,跳转时统一引用。

分包的另一个隐藏好处是首屏加载更快。因为小程序启动时只加载主包,商品详情这种大页面在用户点进去的时候才加载,体感上流畅很多。答辩的时候你可以把"分包加载"作为技术亮点讲,这个点比"我用了flex布局"要有分量得多。

2.3 顶部导航栏高度:自定义导航与机型适配

商城类小程序对顶部导航的要求比较特殊。米家商城的首页顶部是一个搜索框,而不是默认的"微信小程序标题栏"。如果用系统默认导航栏,只能显示一个固定在左上角的胶囊按钮,标题栏背景色可以配,但搜索框、城市定位这些都得放在页面里,很容易出现搜索框顶着状态栏、或者视觉上高低不齐的问题。

因此我选择自定义导航栏。关键是拿到状态栏高度和胶囊按钮位置。

// 获取状态栏高度(单位px) const systemInfo = wx.getSystemInfoSync(); // 胶囊按钮位置信息 const menuButton = wx.getMenuButtonBoundingClientRect();

wx.getMenuButtonBoundingClientRect()这个API很关键,它会返回胶囊按钮的 top、right、width、height。自定义导航栏的高度计算,业界常用公式是:

导航栏高度 = (胶囊top - 状态栏height) * 2 + 胶囊height

这个公式的原理是:胶囊按钮在垂直方向上是居中的,胶囊顶部到状态栏底部的距离,和胶囊底部到导航栏底部的距离是对称的。所以导航栏总高度就是"上间距 + 胶囊高度 + 下间距",而上间距等于“胶囊top - 状态栏height”。

实操里,我会把这段逻辑抽成一个工具函数:

function getNavBarInfo() { const systemInfo = wx.getSystemInfoSync(); const menuButton = wx.getMenuButtonBoundingClientRect(); const statusBarHeight = systemInfo.statusBarHeight; const navBarHeight = (menuButton.top - statusBarHeight) * 2 + menuButton.height; return { statusBarHeight, navBarHeight, menuButton }; }

开发时在页面用padding-top: {{statusBarHeight}}px撑开状态栏,再用height: {{navBarHeight}}px放导航内容。这个代码看起来简单,但做毕设时很容易踩一个真机坑:wx.getSystemInfoSync在部分安卓机型上拿到的statusBarHeight是过时的,尤其是小屏手机横竖屏切换之后,所以现在更推荐用wx.getWindowInfo()来获取。老API虽然还能用,但你写论文的时候建议标注清楚:"本项目使用新版API替代已废弃的getSystemInfo"。

3. 核心功能实现:商品列表加载更多、SKU选择与购物车

3.1 页面列表加载更多:不要再用onReachBottom无脑兜底

"页面列表加载更多"是我从热搜词里看到最高频的一个问题,也是每个电商小程序必做的核心交互。发起一个商品列表,不可能一次性加载1000条,所以要做分页。微信小程序里触发加载更多的传统方式是onReachBottom,也就是页面滚动到底部时触发。但你要是真做一个商城项目,只靠这个远远不够,因为页面底部可能还有其他内容,比如"为您推荐"模块,用户滑到底部后先看到推荐还是先触发加载,体验很微妙。

我的做法是:列表滚动区域独立出一个scroll-view,用scroll-view的bindscrolltolower来触发加载。注意scroll-view必须设置固定高度,否则不会触发滚动。我在代码里用了height: calc(100vh - 顶部高度)来撑开列表区域。

分页请求的核心参数有三个:page、pageSize、hasMore。我强烈建议不要只用一个page判断是否还有下一页,而是后端返回total或者hasNext。因为商城列表如果筛选了分类,总数会变化,page单纯加1没法准确判断边界。

Page({ data: { goodsList: [], page: 1, pageSize: 10, hasMore: true, loading: false }, async loadGoodsList() { if (this.data.loading || !this.data.hasMore) return; this.setData({ loading: true }); const res = await request({ url: '/api/goods/list', data: { page: this.data.page, pageSize: this.data.pageSize } }); const list = res.data.list || []; this.setData({ goodsList: this.data.goodsList.concat(list), page: this.data.page + 1, hasMore: res.data.hasMore, loading: false }); } });

重点讲三个防坑点:

第一,防重复请求。用loading标志位,当下拉加载还在请求中,用户再触发一次就直接return,否则会出现数据翻倍、顺序错乱。

第二,列表渲染时的wx:key一定要用唯一id,我见过有人直接用wx:key="index",这在纯前端写死的Demo里没问题,但只要列表涉及增删、排序,用index当key会导致组件状态错乱。

第三,加载更多的UI状态。要让用户明确感知"正在加载"还是"没有更多了"。底部通常有三种状态:加载中显示"加载中...",加载完成且还有更多显示"上拉加载更多",没有更多显示"已经到底了"。这个状态不要拼在一个字符串里,用loadStatus字段控制更清晰。

3.2 SKU选择弹窗:规格组合背后的数据模型

米家商城的商品很多是多规格的,比如智能灯的亮度、色温、套餐版本。SKU(Stock Keeping Unit,库存量单位)选择是电商项目里中等偏难的功能。我建议毕设至少要做出"S1 + S2 两种规格组合出唯一SKU"的逻辑,不要只做单选。

这里我贴一个简化的SKU数据结构:

// 商品详情返回的SKU列表 skus: [ { skuId: 1, color: "白色", version: "标准版", price: 199, stock: 50 }, { skuId: 2, color: "白色", version: "礼盒版", price: 229, stock: 20 }, { skuId: 3, color: "灰色", version: "标准版", price: 199, stock: 0 }, { skuId: 4, color: "灰色", version: "礼盒版", price: 229, stock: 10 } ]

当用户选中color="白色",还没选version时,应该把可选的version优先展示出来,如果某个组合库存为0(比如灰色标准版),那个按钮要置灰。

这种联动逻辑我建议用一个已经验证过的算法思路:把SKU列表转成一个"规格属性 -> 可选规格值"的映射。每次选中一个属性,就遍历所有SKU,判断包含当前已选组合的SKU有哪些,再把这些SKU的可选值合并,得到下一级可选规格。

很多同学习惯性用三层if嵌套来判断,代码写出来又臭又长,而且一旦加第三个规格(比如"带充电器/不带充电器"),整个判断就要推翻重写。用映射表加遍历的方式,扩展性会好很多。答辩时这段数据模型可以重点讲,老师只要看到你不是"写死规格"的,基本就能认定项目有设计感。

3.3 购物车的选中态与价格联动

购物车这个页面看起来很普通,但实现时有一个细节容易忽略:入库购物车的是什么?是商品还是SKU?

正确的做法是:购物车列表的每一项应该对应一个SKU,而不是一个商品。因为你选了"白色 + 标准版",和"灰色 + 礼盒版"是两个不同的SKU,价格也可能不同。如果购物车只存goodsId,用户改了规格之后价格就乱了。

购物车需要保存的字段我建议至少包含:

cartItem: { cartId, // 购物车条目id goodsId, // 商品id skuId, // 规格id goodsName, skuSpecText, // "白色 标准版" price, count, checked }

价格联动的核心就是:选中的购物车条目发生变化时,重新计算总价。这个在WXML里可以用watch或者计算函数,但我更推荐在setData更新购物车列表后,单独调用一个calcTotalPrice()函数,把总价写在data里。不要在模板里写{{totalPrice}}依赖多个字段的复杂计算,那会很难调试。总价永远保存成单独字段,这是一个很朴素的建议,但能救你命。

购物车里还有一个对毕设特别重要的点:空购物车视图。米家商城这种大厂产品,是不会让新用户看到一个白屏购物车的。你要在购物车里判断cartList.length === 0时,显示"购物车还是空的,快去逛逛"搭配一个去首页的按钮。别小看这个,很多毕设项目,空状态没有做,用户把商品删光之后页面直接空白,答辩演示时老师一操作就露怯。所有列表类页面(订单、优惠券、消息)都要做空状态。

4. 微信登录与会话保持:从wx.login()到自定义token的完整链路

4.1 为什么不能只靠wx.getUserProfile

微信小程序的登录体系,最容易踩的坑就是——旧项目用的wx.getUserProfile和wx.getUserInfo拿用户头像昵称,到了新版本经常拿不到真实数据,因为微信在2022年之后调整了隐私接口规则,现在获取用户头像昵称,必须通过头像昵称填写能力(input type="nickname"和头像选择组件)引导用户自主填写。所以如果你还在代码里写wx.getUserProfile({ success: ... }),直接给用户一个弹窗授权,这个思路已经行不通了。

正确登录链路是这样的:

前端 wx.login() 获取code → 发送到后端(或云函数) → 后端调用微信接口 code2Session 换取 openid 和 session_key → 生成自定义token(自己造的登录态)返回前端 → 前端把token存入storage → 后续请求带上 token,后端验证token

先看第一步:wx.login()。注意,wx.login()拿到的code有效期只有五分钟,且只能使用一次,它本身不是登录凭证,而是换取 openid 的临时票据。很多同学误以为拿到code就可以当token用,这是不对的。

如果使用云开发,可以用云函数来简化:

// 云函数 login const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event) => { const { OPENID, APPID } = cloud.getWXContext() const db = cloud.database() const userTable = db.collection('users') let user = await userTable.where({ openid: OPENID }).get() if (user.data.length === 0) { // 新用户,写入用户记录 await userTable.add({ data: { openid: OPENID, createTime: Date.now() } }) } // 生成一个自定义登录态,用openid+时间戳做摘要 const token = `${OPENID}_${Date.now()}` return { token, openid: OPENID } }

这里我故意没写真正的JWT加密,因为毕设阶段,只要你有一个不透明的token、后端能校验,就已经满足"会话保持"的课程设计目标了。但如果你有余力,可以引入JWT,把openid作为payload的一部分。这在论文里的写法就是"采用JWT方案维护用户态状态,避免明文传递openid"。

4.2 token过期与401刷新

后端返回token时,一定要考虑过期时间。我非常推荐在响应头或者响应体里带一个expire时间,比如7200秒。前端在请求拦截器里做统一处理:

const request = (options) => { return new Promise((resolve, reject) => { const token = wx.getStorageSync('token'); wx.request({ ...options, header: { 'Authorization': `Bearer ${token}`, ...options.header }, success(res) { if (res.statusCode === 401) { // token过期,重新登录 wx.removeStorageSync('token'); handleLoginRedirect(); return; } resolve(res.data); }, fail: reject }); }); };

这个401拦截是所有依赖登录态接口的生命线。如果没有做,用户token过期后,所有请求都会莫名其妙返回错误,页面显示一堆"网络异常",到时候你排查半天根本定位不到是登录态问题。把401统一处理掉,整站的登录体验就稳了一截。

4.3 登录按钮与个人信息的交互设计

米家商城的"我的"页面,登录前一般显示一个占位头像和"点击登录"。这里我建议直接用button组件,开放能力设为bindtap自己实现wx.login,而不是用open-type="getPhoneNumber"来拿手机号。手机号接口现在也是隐私接口,而且毕设用不上短信验证码,绑定手机号的意义不大,没必要给自己增加工作量。

如果项目需要显示用户昵称和头像,不要用原来的授权弹窗方式,改用官方推荐的昵称填写。具体到小程序里,就是放一个input标签,设置type="nickname",头像则用一个按钮让用户调用chooseAvatar上传。这里我不展开贴完整代码,但你要记住,这是2023年后小程序个人中心的标准写法,网上很多老教程已经过时了。

5. 支付模块的毕设级实现:从微信支付到模拟支付的降级方案

5.1 微信支付为什么难:不是你写得慢,是你申请不下来

支付是整个毕设里最特别的一环,因为它不仅仅是代码问题。要在自己的小程序里真正拉起微信支付,必须满足几个硬条件:小程序已完成微信认证、主体是企业或者个体工商户(个人主体不支持开通微信支付)、并且要申请微信支付商户号、绑定AppID并签署协议。对于大多数在校本科生来说,个人身份根本申请不下来。就算申请下来,答辩现场用真钱支付也不现实,总不能为了演示让老师转你一块钱。

所以我对毕设项目的建议是:代码按真实微信支付流程写,但运行环境切成"模拟支付"。

真实支付的下单链路是:

前端请求后端创建订单 → 后端调用微信支付统一下单API(参数有openid、订单号、金额、回调地址) → 微信返回 prepay_id → 后端将 prepay_id 和签名参数返回前端 → 前端调用 wx.requestPayment(参数) 拉起支付面板 → 用户付款成功,微信服务器回调后端通知支付结果 → 后端修改订单状态

这些流程你在论文里可以写清楚,代码也可以预留wx.requestPayment的调用位置。但在开发环境,我建议做一个payType开关:当payType === 'mock'时,点击"立即支付",直接弹窗提示"模拟支付成功",然后把订单状态改成已支付。

async handlePay(orderId) { const payType = this.data.payType; // 通过配置控制:mock / real if (payType === 'mock') { wx.showModal({ title: '模拟支付', content: '演示环境确认支付成功?', success: async (res) => { if (res.confirm) { await request({ url: '/api/order/pay', method: 'POST', data: { orderId } }); wx.showToast({ title: '支付成功', icon: 'success' }); } } }); return; } // 真实支付逻辑:调后端获取支付参数,再 wx.requestPayment }

为什么建议写一个开关而不是直接注释掉真实支付?因为你答辩时如果老师问你"微信支付怎么做的",你直接展示模拟支付,老师会质疑你没做过真实流程。但如果你代码里保留了完整的真实支付函数,并且能讲清楚统一下单、回调验签、订单状态同步,这就是一个"工程降级"的思路。反而显得你有抗风险意识。

5.2 订单状态机:从待付款到待收货的状态流转

支付这块还有一个必做项,就是订单状态。订单状态不要用字符串到处传,我推荐用数字枚举:

const OrderStatus = { CREATED: 10, // 已创建/待付款 PAID: 20, // 已支付/待发货 SHIPPED: 30, // 已发货/待收货 FINISHED: 40, // 已完成 CANCELED: 0 // 已取消 };

状态机是电商项目里"低成本高收益"的设计。你在数据库里存订单状态,如果存的是中文"待付款",后面想做统计、做筛选都会很别扭。用数字枚举,前端再映射成对应的中文文案,逻辑清晰很多。

状态流转要写死在代码里,不能出现"用户点一下确认订单,订单状态就从待付款跳到已完成"这种情况。正常的流转是:创建订单 -> 支付成功 -> 卖家发货 -> 用户确认收货 -> 完成。其中取消订单只能在待付款状态执行。你在答辩时画一张简单的状态图(代码注释里也可以用,但不能用mermaid,直接文字描述即可),老师一看就懂。

5.3 确认订单页:地址选择与库存校验

确认订单页是我觉得整个项目里"细节密度"最高的页面。要干的事有:选择收货地址、展示商品清单、计算运费、计算总价、提交订单。

地址选择这一块,如果做全套,要用wx.chooseAddress获取微信收货地址(需要用户授权),也可以自己做地址管理CRUD。我当时选了后者,因为毕设要体现"数据库增删改查",地址管理正好是一组完整的CRUD,还能顺带展示表单校验。地址列表存在云数据库的addresses集合里,用户新增地址时做简单的字段校验,比如手机号正则、姓名非空。

提交订单前一定要做库存校验。原因很简单:如果用户在商品详情页停留了很久,等到下单时,他选的那个SKU可能已经被别人买完或者库存少了,这时再拿旧库存创建订单,后面发货环节一定会出问题。所以在后端的"创建订单"接口里,先查SKU库存,再减库存,而且要保证这两个操作在同一事务里。云开发数据库支持事务,微信云开发有db.runTransaction;如果你用自建后端,那就在MySQL里用FOR UPDATE行锁。

6. 数据统计、云开发与答辩展示:把"工作量"讲成"系统设计"

6.1 为什么我推荐云开发而不是自建后端

先讲结论:毕设用微信云开发(云函数 + 云数据库 + 云存储),能省掉后端部署、服务器、域名备案的一长串麻烦。你只需要在微信开发者工具里开通云开发,就会得到一个免费的测试环境,云函数直接写JavaScript,数据库是文档型,存储放商品图片,前端用wx.cloud.callFunction调用云函数。

这个选型最大的好处是:整个系统都在微信生态内,不需要额外维护服务器,答辩演示不容易翻车。万一现场网络不行,云端服务全都不可用,那才是灾难。当然,云开发也有缺点,比如不能直接用传统的关系型SQL做复杂的联表查询。但对于商城这种业务来说,可以通过"冗余字段"解决:商品列表接口直接返回需要的商品名、价格、主图,而不是像MySQL那样用join去关联三张表。

我在做米家商城的时候,后端接口是这样拆的:

  • goodsService:商品列表、商品详情、搜索
  • cartService:购物车增删改查
  • orderService:创建订单、订单列表、订单详情、取消/支付/收货
  • userService:登录、地址管理

每个服务对应一个云函数目录,云函数内部再调云数据库。这样一个云函数就是一个小模块,后续好维护。对应论文里,你就可以画一个后端微服务划分图(文字形式),比那些一个云函数里塞1000行代码的毕设强太多。

6.2 云数据库集合设计,直接照抄可用

我把数据库集合结构和关键字段列在这里,你可以直接参考:

goods(商品集合)

{ _id: "商品id", name: "米家智能台灯", category: "台灯", mainImage: "cloud://xxx/xxx.jpg", images: ["...", "..."], price: 199, originalPrice: 259, sales: 1000, skus: [ { skuId, spec, price, stock } ], detail: "富文本描述", status: 1 // 1上架 0下架 }

orders(订单集合)

{ _id: "订单id", orderNo: "20240501123456", userId: "openid", address: { name, phone, province, city, detail }, goodsList: [ { goodsId, skuId, name, specText, price, count, image } ], totalAmount: 499, status: 10, createTime: Date.now() }

cart(购物车集合):注意我建议购物车数据放云数据库而不是本地storage,这样换设备购物车不丢,答辩也更像真实系统。每个购物车条目一个文档,字段参考前面讲的cartItem。

addresses(地址集合):当前用户的所有地址,isDefault标记是否默认地址。

这里特别提醒一个云数据库坑:**在云函数里读数据库,权限不受前端权限限制,所以判断用户数据归属时,要自己在代码里加where({ userId: openid })过滤。**如果你把权限设成"所有人可读",就会出现一个用户能读到别人订单的严重安全问题。这个细节在答辩时老师大概率会问,你要主动讲:"云数据库的权限控制默认是仅创建者可读写,但这不是绝对安全的,我在云函数中统一根据openid做数据隔离。"

6.3 搜索与分类:从分类筛选到关键词搜索的降级方案

真正的米家商城搜索,有搜索历史、热门搜索、联想建议。毕设项目中我建议做"搜索历史 + 结果列表"就够。搜索历史可以存在本地wx.setStorageSync,不用走后端。好处是省一个集合、也不用处理用户画像,坏处是不算真正的"用户行为记录数据库设计",但毕设重心不在这里。

分类页我用的是一个典型的电商"左侧一级分类 + 右侧商品列表"布局。左侧scroll-view显示一级分类,右侧用scroll-view显示该分类下的商品。这个页面看起来结构简单,但有一个性能细节:右侧商品列表图片多,一定要在image标签上设置lazy-load属性,并且控制一次渲染的数据量。千万不要在右侧一次性渲染全部商品,先用分类id请求接口,再做"加载更多"。

分类数据可以这样设计:

categories: [ { _id: "c1", name: "智能家居", children: ["智能音箱", "智能传感器"] }, { _id: "c2", name: "生活电器", children: ["吸尘器", "加湿器"] } ]

点击左侧一级分类时,右侧展示对应的二级分类tab,再点击二级分类,请求对应商品列表。这个交互相对完整,页面不算复杂,也很适合作为答辩讲解的一个模块。

7. 上线前后最容易翻车的五个细节,以及我踩过的坑

7.1 图片资源本地化是第一个大坑

很多同学做商城项目,图省事直接在项目里塞几十张商品图,一张图500KB,整个项目瞬间超过2MB。更稳妥的做法是:图片全部传到云存储,代码里只存cloud://开头的路径。如果你还没有云开发环境,也可以用临时图床,但答辩现场还是建议用云存储,因为cloud://路径在开发者工具里可直接预览,在真机上也能正常加载。本地图片只保留tabBar图标和默认占位图。

7.2 页面栈溢出:不要一直用navigateTo

小程序页面栈最多10层,超过之后wx.navigateTo会失效。电商项目里,用户从首页进入商品列表,再进商品详情,再进确认订单,然后返回,再进订单详情……如果每次都navigateTo,很容易超过10层。我建议:列表进入详情用navigateTo,但详情跳确认订单用redirectTo(关闭当前页)。因为从确认订单还能返回详情没有太大意义,这能避免页面栈堆积。

7.3 富文本详情的解析问题

商品详情页要展示长图或富文本。最简单的方式是后端直接返回一段HTML富文本,前端用rich-text组件渲染。但这里有个坑:rich-text不支持部分HTML标签,例如section可能解析异常。如果你从别处复制的富文本,可能样式全乱。我在做的时候,把商品详情图片单独存数组,详情页用swiper轮播图加图片列表来展示,反而不容易出问题。

7.4 真机调试和开发者工具的差异

开发者工具上正常的CSS,真机上可能会不一样。最常见的几个差异是:

  • flex布局中gap属性在旧版安卓WebView上不支持,建议用margin代替
  • 吸顶效果用position: sticky在部分iOS版本有问题,我用scroll-view的sticky-header属性解决
  • 页面滚动和scroll-view嵌套的时候,滚动会互相干扰,要设置scroll-y并注意高度控制

我建议至少在答辩前一周开始,天天用真机预览调试,不要最后一天才拿手机测试。

7.5 微信小程序年审、域名与体验版

关于微信小程序年审:个人主体小程序认证是免费的,但如果是学校要求用企业主体,年审要交300元左右费用。毕设一般不用考虑年审,但如果从申请注册到开发,再到答辩体验版测试,你最好能有一个已经注册好的小程序AppID。没有AppID也可以用测试号,但测试号的功能有限,比如支付、云开发都可能受限,所以尽量提前用学校或者自己的身份注册一个小程序账号。

域名这块更要注意:如果在云开发模式下,请求都是wx.cloud.callFunction,不需要配置request合法域名;但如果你用的是自建后端,就必须在小程序管理后台配置request合法域名,且该域名必须备案。这就是为什么我反复建议云开发的原因之一,省事。

8. 论文与复盘:这套商城走完,我沉淀了什么

看到这里,其实你已经拥有一套可以照着写的米家商城毕设方案了。最后我想说一点和大作业完全不同的东西:毕设做小程序,代码写得漂亮是一方面,更重要的是能把"为什么这样设计"讲清楚。

我做这个项目时,最大的感受就是小程序开发的上手门槛真不高,但它逼着你去想一个完整业务链路的边界。比如下了单没付款怎么办?付款了没发货怎么办?发货了用户不确认收货怎么办?这些问题不是一个前端页面能解决的,背后全是状态机、数据一致性、权限控制这些软件工程课上学过的东西。

答辩的时候你可以这样讲主线:以微信小程序为前端载体,利用云开发构建了商品管理、购物车、订单、支付模拟的一体化系统,重点解决了SKU多规格选择、列表分页加载、用户登录态保持等实际问题。这一段话朴实无华,但每一环你都真的动过手,和背题库的完全不一样。

最后分享一个小技巧:我会在项目根目录放一个README.md,把启动步骤、目录结构、功能清单、测试账号(如果有)全部写清楚。这个文件不仅方便你过两周再看自己代码,答辩前给老师看一眼也会有加分效果。

这套项目从0到1走完,代码量大概五千行上下,工作量恰好卡在毕设的合理区间。剩下你要做的,就是先把环境搭好,从 tabBar 和首页开始,一步步把清单上的功能划掉。做毕设没有捷径,但每多做一个功能,你离"能讲清楚项目"就更近一步。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询