这次我们来看一个微信小程序实战项目:小米商城小程序。
这个项目最大的特点是“全链路电商流程”:首页、分类、商品列表、商品详情、购物车、订单、个人中心,再加上一个后台管理系统,把商品的增删改查、订单状态管理都串起来了。对于准备毕设、找前端实习、或者想系统性练习小程序的开发者来说,是比较典型的一套练手项目。
先说几个关键信息:
- 技术栈以微信小程序原生为主,后台可以选 Node.js 后端或者直接用小程序云开发;
- 项目不是只写静态页面,而是带真实数据交互,登录、商品查询、购物车、订单提交都能跑通;
- 后台管理系统涉及完整的增删改查,适合用来讲清“前端页面 + 接口 + 数据表”的联动关系;
- 整个项目既适合课程设计、毕业设计,也适合整理成面试项目经验。
这篇文章会按“项目定位 -> 功能模块 -> 环境准备 -> 启动运行 -> 核心功能实现 -> 增删改查设计 -> 接口封装 -> 常见问题 -> 毕设答辩和面试要点”的顺序展开,尽量把能直接复用的代码和思路都写出来。
1. 项目核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 微信小程序商城项目 + 后台管理系统 |
| 技术栈 | 小程序原生 WXML/WXSS/JS,后端可选 Node.js/Express 或微信云开发 |
| 核心功能 | 首页装修、商品分类、商品列表、商品详情、购物车、订单、个人中心 |
| 后台管理 | 商品管理、分类管理、订单管理,覆盖增删改查 |
| 是否需要服务器 | 使用云开发就不需要自建服务器;使用自建后端需要服务器或本地运行 |
| 启动方式 | 微信开发者工具导入项目,配置 AppID 即可预览 |
| 是否支持真机预览 | 支持,真机调试需要配置合法域名或开启调试模式 |
| 适合场景 | 毕设、课程设计、前端求职面试项目、小程序入门综合练习 |
| 开发难度 | 中等,适合已经掌握 HTML/CSS/JavaScript 基础、想进阶小程序的开发者 |
从功能上看,这不是一个“只展示页面”的静态仿站,而是把“用户端小程序 + 管理端后台 + 数据接口”三部分串起来的完整项目。这也是很多面试官比较认可的项目结构:不是只有页面,而是有真实的业务数据流转。
2. 适用场景与使用边界
2.1 这个项目适合谁
- 正在准备毕业设计,需要做一个“前端 + 后台 + 数据库”完整闭环的学生;
- 前端已经入门,想通过一个完整项目搞清楚“小程序如何调用接口、如何管理登录态、如何实现购物车”的开发者;
- 正在准备前端实习或初级前端岗位面试,需要一个能讲清楚业务逻辑和细节的实战项目;
- 想学习“用户端 + 管理端”双端结构,为以后做后台管理系统打基础的人。
2.2 能解决什么问题
这个项目本身的价值,是让你在真实业务场景里理解前端工程化的基本功:
- 小程序页面路由和组件化拆分;
- 请求封装、拦截器、登录态管理;
- 列表页、详情页、购物车、订单的状态管理;
- 后台管理系统的增删改查设计;
- 前后端接口联调的基本流程。
对于面试来说,常见的问题,比如“购物车选中状态怎么维护”“订单状态怎么流转”“小程序登录流程是什么”,都可以在这个项目里实际验证。
2.3 使用边界与合规提醒
小米商城是一个真实商业品牌,做这个项目时要注意几点:
- 学习阶段可以仿照页面布局风格,但建议使用占位图和自定义素材,不要直接使用小米官方 Logo、商品图片和文案做商用或公开演示;
- 如果是毕业设计或课程设计,尽量在项目说明里写清楚“此项目仅为学习交流,素材来源于网络或自绘,不用于商业用途”;
- 如果涉及真实用户手机号、收货地址、订单数据,必须说明数据存储和访问边界,不能把真实用户信息随意公开;
- 后端接口上线时必须配置 HTTPS 合法域名,不能使用明文传输敏感信息;
- 代购、刷单、虚假交易等行为一律不要出现在项目演示里。
守住这几个边界,这个项目用于学习和面试是没有问题的。
3. 项目功能模块与页面结构
整体功能模块可以分成两条线:一条是用户端小程序,另一条是后台管理系统。
3.1 用户端小程序
| 模块 | 核心功能 | 涉及页面 |
|---|---|---|
| 首页 | 轮播图、金刚区入口、推荐商品列表 | pages/home/home |
| 分类 | 左侧分类导航,右侧商品列表 | pages/category/category |
| 商品列表 | 按关键词搜索、按分类筛选、排序 | pages/goods/list |
| 商品详情 | 商品图片、价格、规格选择、加入购物车 | pages/goods/detail |
| 购物车 | 商品勾选、数量加减、金额统计、删除 | pages/cart/cart |
| 订单确认 | 收货地址、订单金额、提交订单 | pages/order/confirm |
| 订单列表 | 订单状态展示、取消订单、确认收货 | pages/order/list |
| 个人中心 | 用户信息、我的订单入口、收货地址管理 | pages/user/user |
| 登录 | 微信授权登录,获取 openid 后换取 token | pages/login/login |
3.2 后台管理系统
| 模块 | 核心功能 |
|---|---|
| 商品管理 | 商品新增、编辑、上下架、删除、分页查询 |
| 分类管理 | 分类的新增、重命名、排序、删除 |
| 订单管理 | 订单查看、发货处理、订单状态更新 |
| 用户管理 | 用户列表查看(一般不允许随意删除,需要权限控制) |
后台管理系统建议用 Vue3 + Element Plus 或者 React + Antd 来写,数据接口和小程序共用同一套后端。这样你的项目亮点会明显很多,因为“前后端一体”比“只写小程序”更有说服力。
4. 环境准备与前置条件
4.1 系统与软件要求
- 操作系统:Windows / macOS 均可;
- 微信开发者工具:最新稳定版即可,建议从微信公众平台官网下载;
- 小程序 AppID:在微信公众平台注册小程序账号后,在“开发管理 -> 开发设置”里获取,也可以使用测试号;
- 后端环境(自建后端方案):
- Node.js 16 或以上版本;
- npm 或 yarn;
- MySQL 或 MongoDB(二选一);
- Postman 或 Apifox 用于接口自测;
- 云开发方案:
- 不需要自建服务器;
- 在开发者工具里开通云开发,创建环境即可。
4.2 开发者工具关键配置
打开项目之前,确认三件事:
- AppID 有没有填对;
- 是否端口开放,允许开发者工具调用本地接口;
- 本地调试时是否勾选了“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。
操作路径:
微信开发者工具 -> 详情 -> 本地设置 -> 勾选“不校验合法域名...”这个选项只用于本地开发,真机上不能依赖它。真机预览时仍然要求接口是 HTTPS 合法域名。
4.3 数据表设计建议
无论用 MySQL、MongoDB 还是云数据库,核心数据表建议拆分如下。
商品表 goods:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | int / string | 主键 |
| name | varchar | 商品名称 |
| cover | string | 封面图 |
| price | decimal | 价格 |
| stock | int | 库存 |
| categoryId | int | 分类 ID |
| status | int | 1 上架,0 下架 |
| createTime | datetime | 创建时间 |
| updateTime | datetime | 更新时间 |
分类表 category:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | int | 主键 |
| name | varchar | 分类名称 |
| sort | int | 排序值 |
订单表 orders:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | int | 主键 |
| orderNo | varchar | 订单号 |
| userId | int | 用户 ID |
| totalAmount | decimal | 总金额 |
| status | int | 0 待付款,1 待发货,2 待收货,3 已完成 |
| address | text | 收货地址快照 |
| createTime | datetime | 创建时间 |
订单商品表 order_items:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | int | 主键 |
| orderId | int | 订单 ID |
| goodsId | int | 商品 ID |
| goodsName | varchar | 商品名称快照 |
| price | decimal | 购入价格快照 |
| count | int | 数量 |
注意“快照”两个字。订单里的商品名称、价格必须从商品表冗余一份到订单明细,否则商品改名或改价后,历史订单就乱套了。
5. 项目初始化与运行方式
5.1 小程序端初始化
以原生小程序为例,项目结构建议如下:
miniprogram/ ├── app.js ├── app.json ├── app.wxss ├── utils/ │ └── request.js ├── components/ │ ├── goods-card/ │ └── empty-view/ ├── pages/ │ ├── home/ │ ├── category/ │ ├── goods/ │ ├── cart/ │ ├── order/ │ └── user/在app.json里注册页面和底部 TabBar:
{ "pages": [ "pages/home/home", "pages/category/category", "pages/cart/cart", "pages/order/list", "pages/user/user" ], "window": { "navigationBarTitleText": "小米商城", "navigationBarBackgroundColor": "#ff6700", "navigationBarTextStyle": "white" }, "tabBar": { "color": "#999999", "selectedColor": "#ff6700", "list": [ { "pagePath": "pages/home/home", "text": "首页" }, { "pagePath": "pages/category/category", "text": "分类" }, { "pagePath": "pages/cart/cart", "text": "购物车" }, { "pagePath": "pages/order/list", "text": "订单" }, { "pagePath": "pages/user/user", "text": "我的" } ] } }这是商城类小程序最基础的路由结构。实际项目中,首页、分类、购物车、订单、个人中心五类 Tab 基本是标配。
5.2 后端初始化(Express 示例)
如果选择自建 Node.js 后端,可以用 Express 快速搭建:
mkdir mishop-server cd mishop-server npm init -y npm install express mysql2 cors body-parser创建入口文件:
const express = require('express'); const cors = require('cors'); const bodyParser = require('body-parser'); const goodsRouter = require('./routes/goods'); const orderRouter = require('./routes/order'); const userRouter = require('./routes/user'); const app = express(); app.use(cors()); app.use(bodyParser.json()); app.use('/api/goods', goodsRouter); app.use('/api/order', orderRouter); app.use('/api/user', userRouter); app.listen(3000, () => { console.log('server running at http://localhost:3000'); });启动命令:
node app.js如果使用云开发,可以跳过自建后端,直接在云函数里写接口逻辑,微信端通过wx.cloud.callFunction调用。两种方案都能把这个项目跑起来,区别是自建后端更贴近企业开发,云开发更快。
6. 核心功能实现思路
6.1 小程序登录与登录态管理
商城类小程序必须要登录,核心流程是:
- 前端调用
wx.login获取临时 code; - 把 code 发给后端;
- 后端调用微信接口用 code 换取 openid;
- 后端生成自定义 token 返回给前端;
- 前端把 token 存入
wx.setStorageSync,后续请求带上 token。
前端登录封装:
// utils/auth.js const login = () => { return new Promise((resolve, reject) => { wx.login({ success: async (res) => { if (res.code) { const { data } = await request({ url: '/api/user/login', method: 'POST', data: { code: res.code } }); wx.setStorageSync('token', data.token); wx.setStorageSync('userInfo', data.userInfo); resolve(data); } else { reject(new Error('获取 code 失败')); } }, fail: (err) => reject(err) }); }); }; module.exports = { login };这里的关键点在于:不要把前端拿到的用户昵称头像直接存到数据库作为用户身份标识。微信官方已经调整了用户信息授权策略,wx.getUserProfile不能作为长期登录凭证。正确做法是以后端拿到的 openid 作为用户唯一标识,昵称头像只作为展示信息,而且要通过“头像昵称填写能力”在用户主动触发时获取。
6.2 首页商品数据加载
首页是最能体现“数据驱动页面”的模块。轮播图、金刚区图标、推荐商品都建议通过接口返回,而不是写死在前端代码里。
首页加载逻辑:
Page({ data: { banners: [], categories: [], recommendList: [], loading: true }, onLoad() { this.fetchHomeData(); }, async fetchHomeData() { this.setData({ loading: true }); try { const res = await request({ url: '/api/home/index', method: 'POST', data: {} }); this.setData({ banners: res.data.banners, categories: res.data.categories, recommendList: res.data.recommendGoods }); } finally { this.setData({ loading: false }); } } });对应后端接口:
router.post('/index', async (req, res) => { const [banners] = await db.query('SELECT * FROM banner ORDER BY sort ASC'); const [categories] = await db.query('SELECT * FROM category ORDER BY sort ASC'); const [recommendGoods] = await db.query( 'SELECT * FROM goods WHERE status = 1 ORDER BY createTime DESC LIMIT 10' ); res.json({ code: 0, data: { banners, categories, recommendGoods } }); });6.3 商品列表与搜索
商品列表页需要支持分类筛选、关键词搜索、排序、分页加载。
核心查询逻辑:
router.post('/list', async (req, res) => { const { categoryId = 0, keyword = '', page = 1, pageSize = 10, sort = 'default' } = req.body; let where = 'WHERE status = 1'; const params = []; if (categoryId) { where += ' AND categoryId = ?'; params.push(categoryId); } if (keyword) { where += ' AND name LIKE ?'; params.push(`%${keyword}%`); } let orderBy = 'ORDER BY createTime DESC'; if (sort === 'price_asc') { orderBy = 'ORDER BY price ASC'; } else if (sort === 'price_desc') { orderBy = 'ORDER BY price DESC'; } const offset = (page - 1) * pageSize; const [rows] = await db.query( `SELECT * FROM goods ${where} ${orderBy} LIMIT ? OFFSET ?`, [...params, pageSize, offset] ); const [[{ total }]] = await db.query( `SELECT COUNT(*) AS total FROM goods ${where}`, params ); res.json({ code: 0, data: { list: rows, page, pageSize, total } }); });小程序端分页加载思路:
onReachBottom触发加载下一页;- 用一个
isLoading状态防止重复请求; - 当已加载数量大于等于 total 时,停止继续请求;
- 每次新数据追加到
list后面,而不是覆盖。
6.4 购物车状态管理
购物车是面试高频题,核心是“单选/全选/数量修改/金额计算”。在小程序里,购物车数据建议直接存本地 Storage,同时同步到后端;也可以完全走后端接口。这里给出本地状态计算的核心代码:
const state = { cartList: [], selectedIds: [] }; // 计算选中商品总额 function calcTotal() { let total = 0; state.cartList.forEach((item) => { if (state.selectedIds.includes(item.id)) { total += item.price * item.count; } }); return Math.round(total * 100) / 100; } // 修改数量 function changeCount(id, delta) { const index = state.cartList.findIndex((item) => item.id === id); if (index === -1) return; state.cartList[index].count += delta; if (state.cartList[index].count < 1) { state.cartList[index].count = 1; } this.setData({ cartList: state.cartList, total: calcTotal() }); }比较讲究的做法,是在app.js的 globalData 里维护购物车,页面通过setData刷新。也可以用 MobX 或 Redux,但小程序原生项目里建议先不引入复杂状态库,把原生方法用熟,后面接框架会更顺。
6.5 订单提交流程
订单创建的关键逻辑是“事务”。一定要保证下面几件事原子完成:
- 校验库存;
- 锁定或扣减库存;
- 创建订单主表记录;
- 创建订单商品明细;
- 清空购物车中对应的商品。
以 Express + MySQL 为例,核心逻辑:
const conn = await db.getConnection(); try { await conn.beginTransaction(); // 扣减库存 await conn.query( 'UPDATE goods SET stock = stock - ? WHERE id = ? AND stock >= ?', [count, goodsId, count] ); // 创建订单 const [orderResult] = await conn.query( 'INSERT INTO orders (orderNo, userId, totalAmount, status, address) VALUES (?, ?, ?, ?, ?)', [orderNo, userId, totalAmount, 0, JSON.stringify(address)] ); const orderId = orderResult.insertId; // 批量创建订单明细 for (const item of goodsList) { await conn.query( 'INSERT INTO order_items (orderId, goodsId, goodsName, price, count) VALUES (?, ?, ?, ?, ?)', [orderId, item.goodsId, item.goodsName, item.price, item.count] ); } await conn.commit(); res.json({ code: 0, data: { orderId, orderNo } }); } catch (e) { await conn.rollback(); res.json({ code: 500, message: '下单失败,请稍后重试' }); } finally { conn.release(); }下单接口一定要做“幂等”处理。同一个请求重复提交,不能创建两笔相同订单。常用方案是前端生成一个唯一的requestId,后端用唯一索引兜底。
7. 增删改查业务与后台管理系统设计
7.1 什么是“增删改查”
增删改查是后端接口最基础的能力,缩写成 CRUD:
- C(Create)新增;
- R(Retrieve)查询;
- U(Update)更新;
- D(Delete)删除。
在小米商城这个项目里,最常见的 CRUD 场景是商品管理:
| 操作 | 接口方法 | 接口路径 |
|---|---|---|
| 新增商品 | POST | /api/goods |
| 查询商品列表 | POST | /api/goods/list |
| 查询商品详情 | GET | /api/goods/:id |
| 更新商品 | PUT | /api/goods/:id |
| 删除商品 | DELETE | /api/goods/:id |
7.2 商品管理的后端实现
商品新增:
router.post('/', async (req, res) => { const { name, cover, price, stock, categoryId, status } = req.body; if (!name || !price || stock === undefined) { return res.json({ code: 400, message: '缺少必填字段' }); } await db.query( 'INSERT INTO goods (name, cover, price, stock, categoryId, status) VALUES (?, ?, ?, ?, ?, ?)', [name, cover, price, stock, categoryId, status || 1] ); res.json({ code: 0, message: '新增成功' }); });商品更新:
router.put('/:id', async (req, res) => { const { name, cover, price, stock, categoryId, status } = req.body; await db.query( 'UPDATE goods SET name = ?, cover = ?, price = ?, stock = ?, categoryId = ?, status = ?, updateTime = NOW() WHERE id = ?', [name, cover, price, stock, categoryId, status, req.params.id] ); res.json({ code: 0, message: '更新成功' }); });商品删除:
router.delete('/:id', async (req, res) => { await db.query('DELETE FROM goods WHERE id = ?', [req.params.id]); res.json({ code: 0, message: '删除成功' }); });实际项目中,商品“删除”很少物理删除,更多是逻辑删除,把status改为-1或deleted字段置 1。这样历史订单里的引用关系不会断裂。
7.3 后台管理系统前端界面
后台管理系统可以用 Vue3 + Element Plus 来做,商品管理页面大致包含:
- 搜索区:按商品名称、分类搜索;
- 操作区:新增商品按钮;
- 表格区:商品名称、分类、价格、库存、上下架状态、操作列;
- 弹窗表单:新增或编辑商品;
- 分页组件。
表格操作列示例伪代码:
<el-table :data="goodsList"> <el-table-column prop="name" label="商品名称" /> <el-table-column prop="price" label="价格" /> <el-table-column prop="stock" label="库存" /> <el-table-column label="状态"> <template #default="{ row }"> <el-switch v-model="row.status" :active-value="1" :inactive-value="0" @change="updateStatus(row)" /> </template> </el-table-column> <el-table-column label="操作"> <template #default="{ row }"> <el-button size="small" @click="handleEdit(row)">编辑</el-button> <el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button> </template> </el-table-column> </el-table>这里的核心是“管理端和小程序端共用同一套商品数据”。小程序端看到的商品上下架状态,取决于后台管理里设置的status字段。这就是一个非常直观的“前后端联动”项目讲解点。
8. 接口请求封装与调试
8.1 小程序请求封装
建议写一个统一的request.js,把 baseURL、token、错误处理都集中在里面。
// utils/request.js const request = function (options) { return new Promise((resolve, reject) => { wx.request({ url: getApp().globalData.baseUrl + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', Authorization: wx.getStorageSync('token') || '' }, success(res) { if (res.statusCode === 200) { if (res.data.code === 0) { resolve(res.data); } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }); reject(res.data); } } else if (res.statusCode === 401) { // 登录态失效,跳转登录页 wx.removeStorageSync('token'); wx.reLaunch({ url: '/pages/login/login' }); reject(res.data); } else { reject(res.data); } }, fail(err) { wx.showToast({ title: '网络异常', icon: 'none' }); reject(err); } }); }); }; module.exports = request;在app.js中设置 baseUrl:
App({ globalData: { baseUrl: 'https://your-api-domain.com/api' } });本地开发时,如果后端在本地运行,可以暂时改为http://127.0.0.1:3000/api,同时勾选开发者工具里的“不校验合法域名”。真机预览时,本地 http 接口是连不通的,必须用 HTTPS 域名。
8.2 接口自测建议
推荐使用 Apifox 或 Postman 自测所有接口,重点验证:
- 参数缺失时的报错信息;
- 分页参数异常时的返回;
- token 过期后的返回状态码;
- 并发下单时库存是否正确扣减。
接口自测是很多初学会忽略的点。把接口测试用例整理好,在面试时可以主动说明自己是怎么保证接口质量,这是加分项。
9. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 开发者工具打开项目空白 | AppID 未配置或项目路径不对 | 查看控制台报错 | 使用测试号或正式 AppID 重新导入 |
| 本地接口请求失败,network 报 500 | 后端服务未启动或接口报错 | 查看后端控制台日志 | 先启动后端,再用 Apifox 自测接口 |
| 真机预览连不上本地接口,报 net::ERR_CONNECTION_RESET | 真机不能访问电脑 localhost | 确认手机和电脑是否同网段 | 真机调试必须使用 HTTPS 合法域名,或使用云开发 |
| 提示“url not in domain list” | 请求域名未配置到合法域名列表 | 登录微信公众平台查看 request 合法域名 | 在后台配置合法域名,开发阶段可临时勾选“不校验合法域名” |
| wx.login 拿到 code 但换不到 openid | 后端没有正确调用微信接口或 AppID/Secret 不匹配 | 检查后端日志和参数 | 确认 AppID 和 AppSecret 正确,并在微信后台配置 IP 白名单 |
| 调用 wx.getUserProfile 失败,报 wx1cb4398e1413dce7 | 用户取消授权,或基础库版本过低,或微信策略调整 | 查看具体错误信息 | 使用新版“头像昵称填写能力”,不要强依赖 getUserProfile |
| 购物车总金额计算不对 | 浮点数运算精度问题 | 打印每一步计算结果 | 金额统一用“分”为单位存储和计算,展示时再除以 100 |
| 数据库中文乱码 | 数据表字符集不是 utf8mb4 | 检查连接配置和数据表字符集 | 创建表时指定 utf8mb4,连接串加上 charset=utf8mb4 |
| 下单时库存扣成负数 | 没有做库存校验或没有用事务 | 检查 UPDATE 语句是否有 stock >= count 条件 | 使用条件更新并开启事务,更新后判断受影响行数 |
| 后台管理系统跨域请求失败 | 后端没有开启 CORS | 查看浏览器 Network 报错 | 后端中间件启用 cors,或开发环境配置代理 |
10. 毕设答辩与面试要点
10.1 项目亮点怎么表达
不要只说“我做了小米商城”,要具体讲清楚:
- “我负责小程序端全部页面和与后端联调,后台管理端实现了商品的增删改查和上下架管理。”
- “登录模块采用 wx.login + code 换取 openid,后端生成 token 维护登录态,解决了用户身份识别问题。”
- “商品列表支持分类筛选、关键词搜索、价格排序和分页加载,列表页通过 onReachBottom 实现触底加载。”
- “购物车模块实现了单选、全选、数量增减和金额实时计算,金额以分为单位避免浮点误差。”
- “订单模块使用数据库事务,先扣库存再生成订单,避免超卖和脏数据。”
这些话术不是背出来,而是建立在真的跑通代码的基础上。面试官很可能追问细节,比如“分页加载怎么避免重复请求”“库存扣减失败怎么办”,只有自己写过才能接得上。
10.2 面试高频追问
以下几个追问要提前准备:
- 小程序登录流程完整描述;
- 为什么不能直接把 openid 返回给前端,或者说直接返回有没有风险;
- 购物车选中的商品刷新页面后还在不在;
- 商品列表分页时,如果有新商品插入,怎么避免列表重复或漏数据;
- 后台删除商品时,如果该商品已经有订单,怎么处理;
- 怎么保证并发下单时库存不超卖。
这些问题的答案,在这个项目里基本都能找到,“库存条件 UPDATE + 事务”就是最典型的超卖防法。
10.3 面试时不要踩的坑
- 不要在项目里写没有跑过的“亮点功能”,比如支付模块如果没有真实应用,就不要说“已实现微信支付”;
- 不要说“所有代码都是我写的”,如果参考了开源项目,要说明自己改进了哪些地方;
- 不要只展示页面截图,要准备接口文档和数据库设计说明;
- 如果面试官问“为什么不直接在小程序里展示云端数据库的数据”,要能解释前后端分离和权限控制的原因。
11. 最佳实践与使用建议
11.1 编码规范
- 小程序状态数据统一走
data声明,不要直接给this.data赋值; - 接口返回的金额用“分”存储,前端展示时统一格式化;
- 页面公共样式提取到
app.wxss,组件样式用组件级wxss; - 接口返回格式统一为
{ code, message, data }结构; - 请求统一走封装后的
request.js,不要每个页面都写wx.request。
11.2 数据安全与权限控制
- 后台管理接口必须做登录鉴权,不能允许任何人直接调
PUT /api/goods/:id修改商品; - 小程序端
openid和userInfo不要写入代码或前端存储中明文暴露; - 如果项目要部署上线,所有接口必须走 HTTPS;
- 删除类操作建议用逻辑删除,防止关联数据出错;
- 用户手机号、收货地址是敏感信息,非必要不要无条件返回给前端。
11.3 开发流程建议
推荐按下面这个顺序推进,能很大程度减少返工:
- 先画数据库表结构和接口文档;
- 用 Apifox 把增删改查接口全部调通;
- 先做一个后台管理系统,把商品数据录入;
- 再做小程序端首页和商品列表;
- 再做商品详情、购物车、订单流程;
- 最后做个人中心和登录态优化;
- 整体流程走通后,再补细节和 UI 美化。
把后台管理系统放在小程序前面,是因为数据录入是前端页面的基础,先把“数据源”准备好了,再写用户端页面效率高很多。
12. 总结与下一步
小米商城小程序这个项目,最适合的做法是“先跑起来,再改造”。第一遍可以按照完整流程把登录、商品列表、购物车、订单、后台管理走通;第二遍再根据自己的想法换一个垂直品类,比如图书商城、数码配件商城、生鲜商城,把商品字段、订单状态、首页装修换成自己的业务需求,这样才能真正变成一个有个人辨识度的项目。
最先应该验证的功能,是“小程序端新增商品 -> 后台列表刷出数据 -> 修改库存 -> 小程序详情页同步更新”这条链路,它能把前后端联动的核心逻辑全面覆盖。最容易踩的坑集中在真机调试域名配置和下单事务这两块,建议提前用测试号把流程完整跑一遍。
如果按这套思路做完,这个项目里积累下来的登录鉴权、列表分页、购物车计算、后台 CRUD、订单事务这些能力,完全可以迁移到其他业务系统里,这才是这个项目最大的价值。