
简介一份完整的微信小程序商城源码专为需要快速搭建微信电商平台的开发者与中小商家设计覆盖商品展示、购物车、订单管理、支付接口及用户互动等核心业务模块。压缩包仅412KB共60个文件其中包含24个PNG图片、9个WXSS样式、9个JS逻辑、7个JSON配置与7个WXML页面结构文件还附带MD说明和DOCX源码讲解目录按pages、utils、components等标准结构组织便于对照学习。已有7731人浏览学习适合刚入门小程序或希望参考成熟商城方案的前端开发者。通过研读源码可掌握WXML/WXSS/JS的协作方式理解从页面布局到数据请求、从购物车到微信支付的完整实现链路快速获得一套可直接改建投产的商城基础框架。1. 这个商城源码到底能做什么从首页到支付的一条龙某开发者朋友接了个线下门店的单子要把商品搬上微信小程序。翻了几个网上的模板要么只有前端页面接口全用 mock 数据要么后端是 PHP本地没环境根本跑不起来。最后拿到这份“微信小程序商城完整源码”才算把整条链路看全了首页轮播、分类页、商品详情、购物车、结算、订单列表、个人中心都在后端用 Node.js 写数据库用 MySQL接口是 RESTful 风格。适合三类人想快速把商城小程序上线的个人开发者做外包需要一套能二次开发的底子的拿来做毕设练全栈流程的学生。它能省下从零搭框架的工夫但能不能跑顺还得看配置和环境。2. 技术选型与目录结构先摸清骨架再动手2.1 为什么是原生小程序加 Node.js这份源码的前端没有用 uni-app、Taro 这类跨端框架而是原生微信小程序的标准四件套WXML 写结构、WXSS 写样式、JS 写逻辑、JSON 写配置。跨端框架的好处是一套代码出多端但代价是抽象层多了一层遇到小程序特有的坑时排查链路变长。原生小程序在微信开发者工具里直接编译不需要额外的构建步骤调试最接近底层而且小程序的分包加载、云开发、支付这些能力原生支持永远是最快的。后端用的是 Node.js 加 Express。选择它的理由很实际前后端都写 JavaScript只维护一种语言心智Express 本身很薄路由、中间件、静态文件这些够用不需要像 Java 那套要处理容器和依赖注入。数据库配的是 MySQL表结构里商品表、SKU 表、订单表、用户表、购物车表是分离的订单数据用事务保证一致性。如果你拿到的版本不带 server 目录而是 cloudfunctions 目录那说明这是微信云开发版后端逻辑放在云函数里数据库用云数据库前端不再用 wx.request 打 HTTP 接口而是改用 wx.cloud.callFunction。两者差别主要在接口调用方式上业务逻辑写法差不多。我一般建议新手先跑本地 HTTP 版因为后端日志看得见报错时能直接定位云开发版排查问题多一层间接。2.2 源码目录结构逐层拆解先把项目根目录的骨架用树列出来wechat-mall/ ├── project.config.json // 开发者工具的项目配置AppID、编译设置在这 ├── miniprogram/ // 小程序前端源码 │ ├── app.js // 入口文件初始化全局登录态 │ ├── app.json // 全局页面路由、窗口样式、tabBar 配置 │ ├── app.wxss // 全局样式 │ ├── pages/ │ │ ├── index/ // 首页轮播、商品列表 │ │ ├── category/ // 分类页左侧分类右侧商品列表 │ │ ├── cart/ // 购物车页 │ │ ├── user/ // 个人中心 │ │ ├── goods-detail/ // 商品详情 │ │ ├── checkout/ // 结算页地址、优惠券、支付 │ │ ├── order-list/ // 订单列表 │ │ └── order-detail/ // 订单详情 │ ├── components/ // 通用组件数量选择器、空状态等 │ └── utils/ │ ├── request.js // wx.request 的 Promise 封装 │ └── auth.js // token 的存取与过期处理 └── server/ // Node.js 后端 ├── app.js // Express 入口挂载路由 ├── routes/ // 路由定义goods、cart、order、user ├── controllers/ // 业务逻辑 ├── models/ // 数据库连接与查询 └── sql/ // 建表脚本 mall.sql每个页面目录下是四个同名前缀文件index.js、index.wxml、index.wxss、index.json这是小程序页面的固定结构。页面的 JS 文件里用 Page 函数注册页面WXML 是模板层小程序本身不支持操作 DOM所以改页面内容都是通过 setData 驱动这个心智要先建立起来。server 目录是整个源码的接口层。routes 只做路由分发controllers 写业务逻辑models 做数据访问把 HTTP 层、业务层、数据层分开。想加一个“限时秒杀”模块时只需要在 routes 里加一条路由、controllers 里加一个方法、models 里加一条查询不动其他文件跟小程序前端页面改动互不干扰。2.3 全局配置与页面路由解析小程序入口是 miniprogram/app.json页面注册都在这个文件里看一下具体长什么样{ pages: [ pages/index/index, pages/category/category, pages/cart/cart, pages/user/user, pages/goods-detail/goods-detail, pages/checkout/checkout, pages/order-list/order-list, pages/order-detail/order-detail ], window: { navigationBarBackgroundColor: #ffffff, navigationBarTitleText: 商城, navigationBarTextStyle: black }, tabBar: { color: #999999, selectedColor: #e93b3d, list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/category/category, text: 分类 }, { pagePath: pages/cart/cart, text: 购物车 }, { pagePath: pages/user/user, text: 我的 } ] } }pages 数组里第一个元素是启动页这里把首页放在第一位打开小程序默认进首页。tabBar 只支持 2 到 5 个项这里配置了四个底部导航入口。要注意每个新增页面必须先在 pages 里注册否则 wx.navigateTo 跳转时会报“页面不存在”的错误这个错误很容易在二次开发时踩到。project.config.json 里最关键的是 appid 字段。开发工具加载项目时需要用它识别小程序身份。没有自己的 AppID 时开发者工具支持用测试号但测试号不支持云开发也不支持 wx.requestPayment 支付所以要完整跑通这个商城建议先注册一个小程序账号拿真实 AppID。编译相关的 libVersion基础库版本和 setting是否开启 ES6 转 ES5也放在这个文件里版本别选太低低版本基础库兼容不了某些 API。3. 核心模块拆解从登录到下单的完整链路3.1 登录授权用 code 换 token商城所有接口除了商品浏览外基本都要登录态所以登录是第一个要讲清楚的模块。完整流程是小程序端先调 wx.login 拿到一个临时 code这个 code 5 分钟内有效且只能用一次后端拿着 code 去微信服务端换 openid这是用户在小程序里的唯一标识后端用 openid 查用户表没有就自动注册最后签一个 JWT 返回给前端前端把 token 存进本地缓存。前端登录代码一般放在 app.js 的 onLaunch 里// miniprogram/app.js const request require(./utils/request) App({ onLaunch() { // 每次冷启动都刷新登录态静默登录不弹授权窗 this.login() }, async login() { const codeRes await wx.login() if (!codeRes.code) { console.error(wx.login 拿不到 code) return } const { data } await request({ url: /api/auth/login, method: POST, data: { code: codeRes.code } }) wx.setStorageSync(token, data.token) wx.setStorageSync(userInfo, data.userInfo) } })这里 wx.login 返回的 code 是临时凭证参数 code 是给后端换取 openid 用的别把 code 存起来反复用。后端拿到 code 后要请求微信的 jscode2session 接口把 openid 取出来// server/controllers/auth.js const axios require(axios) const jwt require(jsonwebtoken) exports.login async (req, res) { const { code } req.body const wxRes await axios.get(https://api.weixin.qq.com/sns/jscode2session, { params: { appid: process.env.APPID, secret: process.env.SECRET, js_code: code, grant_type: authorization_code } }) if (!wxRes.data.openid) { return res.status(401).json({ message: code 无效或已过期 }) } const openid wxRes.data.openid let user await UserModel.findByOpenid(openid) if (!user) { user await UserModel.create({ openid, nickname: 微信用户 }) } const token jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: 7d }) res.json({ token, userInfo: user }) }请求 jscode2session 时appid 和 secret 必须从服务端环境变量读取不能写在小程序代码里否则 secret 直接暴露给前端。token 用 JWT 签出来后面每个需要登录的接口都要校验这个 token。后端封装一个 auth 中间件去解析 Authorization 头取不到或过期就返回 401前端拿到 401 时自动清掉本地缓存并重新走登录流程。提示如果你是云开发版本把这一段换成 wx.cloud.callFunction 调用云函数云函数里通过cloud.getWXContext()直接拿到 OPENID省去 jscode2session 这一步。3.2 首页与商品列表的数据流首页一般包含轮播图、分类入口、推荐商品三个区域。请求设计上轮播图和商品列表是两个独立接口。为了减少首屏等待我在 onLoad 里用 Promise.all 并行请求// miniprogram/pages/index/index.js const request require(../../utils/request) Page({ data: { banners: [], goodsList: [], page: 1, hasMore: true, loading: false }, async onLoad() { // 首屏并行拉 banner 和商品第一页互不等待 const [bannerRes, goodsRes] await Promise.all([ request({ url: /api/banner/list }), request({ url: /api/goods/list, data: { page: 1, pageSize: 10 } }) ]) this.setData({ banners: bannerRes.data, goodsList: goodsRes.data.list, hasMore: goodsRes.data.list.length 10 }) }, async onReachBottom() { // 滚动到底部加载下一页page 加 1 后把新数据追加到列表 if (this.data.loading || !this.data.hasMore) return this.setData({ loading: true, page: this.data.page 1 }) const { data } await request({ url: /api/goods/list, data: { page: this.data.page, pageSize: 10 } }) this.setData({ goodsList: this.data.goodsList.concat(data.list), hasMore: data.list.length 10, loading: false }) } })商品列表接口做了分页参数 page 是页码、pageSize 是每页条数。后端返回结构里除了 list 数组外还会带 total 数字前端用 list 的长度判断是否还有下一页。这里有个常见的翻车点如果直接把后端返回的数组覆盖给 goodsList而不是 concat 拼接就会出现下拉加载后只显示新一页的问题所以追加数据一定要用 concat。接口封装统一走 utils/request.js这个文件把 wx.request 包成 Promise顺带把 token 加到 header 里// miniprogram/utils/request.js function request(options) { return new Promise((resolve, reject) { const token wx.getStorageSync(token) wx.request({ url: getApp().globalData.baseUrl options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: token ? Bearer token : }, success(res) { // 后端统一返回 { code, data, message }code 为 0 视为成功 if (res.data.code 0) { resolve(res.data) } else { if (res.data.code 401) { wx.removeStorageSync(token) } reject(res.data) } }, fail(err) { reject(err) } }) }) }封装的好处是调用方不用重复处理 token 拼接和错误码分发。注意这里 baseUrl 是挂在全局 globalData 上的本地联调、测试环境、线上环境地址不一样时改一处全局变量就行不用去每个页面里找字符串。3.3 商品详情与 SKU 规格选择商品详情页是商城转化率最关键的一页。数据上是两个接口一个是商品基本信息标题、主图、详情富文本一个是 SKU 列表规格组合、价格、库存。SKU 是具体到“颜色 尺码”粒度的一条记录比如一件衣服有“白色 M 码、白色 L 码、黑色 M 码”三条 SKU每条都有自己的价格和库存。// miniprogram/pages/goods-detail/goods-detail.js Page({ data: { goods: {}, skuList: [], selectedSku: null, quantity: 1 }, async onLoad(options) { // options 是页面跳转带来的参数这里取商品 id const { id } options const [goodsRes, skuRes] await Promise.all([ request({ url: /api/goods/detail, data: { id } }), request({ url: /api/goods/skus, data: { id } }) ]) this.setData({ goods: goodsRes.data, skuList: skuRes.data }) }, chooseSku(e) { const skuId e.currentTarget.dataset.id const sku this.data.skuList.find(item item.id skuId) // 库存为 0 的 SKU 置灰不可点这里再做一次防御 if (!sku || sku.stock 0) return this.setData({ selectedSku: sku }) }, changeQuantity(e) { const quantity e.detail.value const sku this.data.selectedSku // 加购数量不能超过当前 SKU 库存 if (sku quantity sku.stock) { wx.showToast({ title: 库存不足, icon: none }) return } this.setData({ quantity }) } })WXML 端选择规格时会用>// server/controllers/goods.js exports.getGoodsDetail async (req, res) { const { id } req.query const goods await GoodsModel.findById(id) if (!goods) { return res.status(404).json({ code: 404, message: 商品不存在 }) } res.json({ code: 0, data: goods }) } exports.getGoodsSkus async (req, res) { const { id } req.query const skus await SkuModel.findByGoodsId(id) res.json({ code: 0, data: skus }) }分类页的查询逻辑也是同一个套路先查出分类再查每个分类下的商品列表最后拼成两级结构。我处理 ID 关联时要注意商品表里存的分类 ID 和分类表的主键类型必须一致遇到查出来商品为空的情况八成是类型不匹配或者分类 ID 写死在了前端。SKU 表结构固定下来大概是这几个字段二次开发时照着扩展就行字段说明idSKU 唯一标识goods_id所属商品 IDspec_name规格名如“黑色 M 码”price售价stock库存3.4 购物车、结算与微信支付购物车的实现方式有两种纯本地存储或者本地存储加服务端同步。这份源码里购物车数据先存本地缓存下单前再把选中项提交给后端。这样做的原因是购物车操作频繁每次都请求接口会拖慢页面响应但下单时必须以服务端数据为准避免用户修改本地数据绕过库存校验。购物车的本地存储用 wx.setStorageSync 操作// miniprogram/utils/cart.js const CART_KEY cart function getCart() { return wx.getStorageSync(CART_KEY) || [] } function addToCart(goods, sku, quantity) { const cart getCart() const index cart.findIndex(item item.skuId sku.id) if (index 0) { // 同一个 SKU 已存在累加数量 cart[index].quantity quantity } else { // 不存在则 push 一条skuId 是购物车行的唯一键 cart.push({ goodsId: goods.id, skuId: sku.id, title: goods.title, image: sku.image || goods.mainImage, price: sku.price, quantity }) } wx.setStorageSync(CART_KEY, cart) }这里以 skuId 作为购物车行的唯一键而不是 goodsId因为同一商品的不同规格要分开计数。购物车页面可以修改数量、勾选商品结算时把勾选的行组装成订单提交到后端// miniprogram/pages/checkout/checkout.js async function submitOrder() { const token wx.getStorageSync(token) const cartItems this.data.selectedItems.map(item ({ skuId: item.skuId, quantity: item.quantity })) // 创建订单后端会重新校验库存和价格 const { data } await request({ url: /api/order/create, method: POST, data: { items: cartItems, addressId: this.data.addressId, remark: this.data.remark }, header: { Authorization: Bearer token } }) // 创建订单成功后调用支付接口拿到 wx.requestPayment 所需参数 const payRes await request({ url: /api/pay/wxpay, method: POST, data: { orderId: data.orderId } }) wx.requestPayment({ timeStamp: payRes.data.timeStamp, nonceStr: payRes.data.nonceStr, package: payRes.data.package, signType: RSA, paySign: payRes.data.paySign, success: () { wx.navigateTo({ url: /pages/order-detail/order-detail?id data.orderId }) } }) }下单接口把 items 数组传给后端后端在事务里重新查一次 SKU 库存再算一次总价防止客户端传一个被篡改的价格。这两件事必须在同一个事务里做不然会出现一边扣库存一边没生成订单的脏数据。支付环节是整个链路里最容易出问题的地方。wx.requestPayment 要求后端先调用微信支付统一下单接口拿到 prepay_id再生成签名串返回给前端。这里的 timeStamp 是当前时间戳的字符串、nonceStr 是随机字符串、package 的值固定是prepay_idxxxpaySign 要用商户密钥对这几个参数加签。签名字段名一定要写对package 是小写timeStamp 的 T 和 S 是大写拼错了支付直接失败。注意开发工具模拟器里支付有时能弹窗但真机必须用最新基础库且商户号必须在小程序后台完成关联否则真机一律报错。4. 本地跑通从导入项目到真机预览4.1 环境准备与账号信息在动手之前先确认三样东西齐不齐小程序账号、微信开发者工具、Node 运行环境。小程序账号需要在小程序管理后台注册注册成功后能看到 AppID 和 AppSecretAppSecret 只在服务端使用保存到后端环境变量里。个人主体账号可以跑通页面和登录但微信支付要求企业主体并且要单独开通微信支付商户号这一步很多新手卡住。开发者工具用稳定版就可以不要用预览版API 行为差异会给排查添乱。Node 建议 14 以上版本MySQL 用 5.7 或 8.0 都可以。后端启动前先确认 MySQL 服务已经起来能通过命令行连上避免一上来就报数据库连接错误干扰判断。4.2 导入源码与初始化数据库先把源码压缩包解压。打开微信开发者工具点“导入项目”目录选择解压后的根目录要确保 project.config.json 在这个目录下AppID 填你自己的。如果导入时提示目录不是小程序项目多半是选错了层级选到了 miniprogram 子目录找到包含 project.config.json 的那一层再导一次。后端和数据库初始化按下面的顺序执行# 进入 server 目录 cd server # 安装后端依赖 npm install # 初始化数据库表结构 mysql -u root -p sql/mall.sql # 创建 .env 配置文件填好数据库账号密码和密钥 cp .env.example .env # 启动后端服务默认监听 3000 端口 npm run dev第一步先建表再启动服务顺序不能反。mall.sql 里除了建表语句还带了初始化的商品和轮播图数据。表建好后后端启动时如果报连接数据库失败优先检查 .env 里的数据库用户名、密码、库名是否和实际一致。npm run dev 跑起来后能看到 Express 打印的监听日志说明后端就绪。4.3 修改前后端关键配置前后端要对上接口地址。小程序端打开 app.js找到 globalData 里的 baseUrl本地联调时改成 http://localhost:3000// miniprogram/app.js App({ globalData: { baseUrl: http://localhost:3000 } })后端 .env 要保留一份自己的配置PORT3000 APPID你的小程序AppID SECRET你的小程序AppSecret JWT_SECRET一段随机字符串 DB_HOST127.0.0.1 DB_PORT3306 DB_NAMEmall DB_USERroot DB_PASSWORD你的数据库密码改完配置后开发者工具里做一个关键动作在工具栏“详情 → 本地设置”里勾选“不校验合法域名”否则所有 http://localhost 的请求都会被拦截。这是开发阶段唯一能让本地 HTTP 接口跑通的方式正式发布前必须把这个选项取消并在小程序管理后台配置 HTTPS 合法域名。4.4 编译、预览与真机调试配置都完成后点开发者工具工具栏的编译按钮。编译过程里注意看 console 面板如果 app.js 里的 wx.login 报错先把基础库版本切到最新稳定版再试。模拟器跑通后用真机预览验证点预览手机扫码打开小程序。这里要注意真机上 localhost 指向手机自己所以要把 baseUrl 改成电脑在局域网里的 IP比如 http://192.168.1.100:3000手机和电脑连同一个 WiFi 才能访问到。改完配置后重新编译再预览一次。如果手机访问不到后端优先查两件事第一电脑防火墙有没有放行 3000 端口第二后端启动时监听的地址是不是 0.0.0.0。Express 默认监听所有网卡如果用了自定义 host 参数不要填 127.0.0.1要留空或填 0.0.0.0。这一步没走通之前别急着换测试环境先把这两点排查完再换。5. 避坑指南源码跑起来前后的常见问题5.1 页面白屏编译不报错现象编译通过console 也没有报错但模拟器页面一片白或者只有一个 tabBar 底栏页面内容全空。原因最常见的是基础库版本太低。app.json 里如果用了高版本才支持的组件或 API低版本基础库会直接渲染失败但不抛业务异常。第二个可能是冷启动时登录接口还在 pending 状态首页 onLoad 里用 await 等 Promise.all 完成期间页面没有渲染出占位 UI。解决先把 project.config.json 里的 libVersion 改成最新稳定版并重新编译如果还白屏看 Network 面板确认接口是否返回接口挂了会导致 onLoad 里 Promise.all reject页面停在初始状态。给首页加一个简单的 loading 占位视图至少用户能看到页面不是死的。这个坑的本质是接口失败和渲染失败叠加查的时候先拆成两个问题分别定位。5.2 接口请求全部 fail还带一串 errno现象进入首页后所有 wx.request 都走 fail 回调提示 request:fail 或者 errno 一个很大的负数后端看不到任何请求日志。原因基本可以断定是开发工具拦截了请求。只要请求地址不是 HTTPS并且没有勾选“不校验合法域名”wx.request 会直接在工具层拦截根本不发出去。另一个原因是 baseUrl 拼错了比如写成了 http:/localhost:3000 少了一个斜杠或者填了带路径的地址导致接口路径重复。解决开发阶段打开工具“详情 → 本地设置”勾选“不校验合法域名”。确认 baseUrl 拼写正确后验证后端服务是否真的起来了直接在浏览器里访问http://localhost:3000/api/goods/list?page1pageSize10能看到 JSON 就说明后端没问题问题只在小程序端到接口这一层。5.3 登录成功但用户信息不刷新现象第一次启动能拿到 token但个人中心页显示的昵称是“微信用户”头像空白退出重进也没变化刷新 token 之后数据还是旧值。原因wx.login 拿到的 code 是一次性的登录接口返回后后端签发的 token 有时效。个人中心的用户信息如果取自登录成功那一刻接口返回的数据而 getUserProfile 授权弹窗是在用户主动点击时才触发那么默认匿名昵称会被缓存下来后续不再更新。另一个原因是本地缓存了旧的 userInfo优先级比接口返回的新值高。解决把昵称头像的存储策略改一下。匿名用户只存 token不存 userInfo个人中心页面每次显示前都调/api/user/profile拉最新数据用户点击授权后把 getUserProfile 返回的昵称头像提交到后端更新。改完数据后要调 wx.removeStorageSync(userInfo) 或者重新 setStorageSync 覆盖不要只改内存变量小程序每个页面都是独立读缓存的。5.4 真机支付时报错“统一下单失败”现象开发工具模拟器里走完下单流程没问题但真机支付时 wx.requestPayment 直接进 fail 回调报统一下单失败或签名错误。原因支付比普通请求多了一整套商户号配置。常见原因有三个第一小程序账号主体和微信支付商户号主体不一致或者商户号没有关联到该小程序第二后端调用统一下单接口时signType 声明了 RSA 但前端传的是 MD5 的签名两边不一致第三package 参数拼错了正确格式是 prepay_idwx...不带这个前缀微信直接拒绝。解决第一确认商户号已经在支付后台完成 AppID 授权绑定。第二前端和后端约定好 signTyperequestPayment 里填signType: RSA时后端必须用 RSA 加签不要混用。第三打印后端返回的所有字段逐字核对参数名。支付签名这个东西出错就是纯玄学唯一的排查方式就是把后端返回的原始报文拿出来到微信支付 API 文档里逐项比对。现在再遇到支付签名错我第一反应就是去翻后端统一下单的日志把返回的原始报文拉出来逐个字符对。5.5 商品图片全部裂开现象商品列表能正常出数据价格文字都有但图片区域一片灰或者直接裂开。原因图片地址写成了后端本机路径类似 http://localhost:3000/uploads/1.jpg。开发工具模拟器能访问本机地址但真机上 localhost 指向手机自己自然加载不到即使改成局域网 IPhttp 协议资源在小程序真机上也会被拦截只能在开发工具里过关。解决后端统一提供上传接口商品图片存到服务器后返回相对路径前端展示时用 baseUrl 拼接完整地址。真机调试阶段先用局域网 IP图片跟着接口地址走就不会裂。正式上线时图片全部走对象存储服务的 CDN 地址配上 HTTPS 域名避免协议拦截导致资源加载失败。6. 二次开发从“能跑”到“能改”6.1 首页轮播改成数据库配置源码首页的轮播图通常写在页面 data 里改起来不灵活。我会先建一张 banner 表字段就三个id、image_url、link_url在 models 里加查询routes 里挂上/api/banner/list前端 index.js 里把写死的 banners 删掉改成从接口拉。接口返回的 image_url 如果是相对路径前端要拼上 baseUrl否则真机图片必裂。6.2 切换到自己后端的接口契约接自己后端时最省事的做法是保持 request.js 不变只改 baseUrl让后端按统一契约对齐返回结构是 { code, data, message }code 为 0 成功非 0 失败401 代表登录态失效。如果你自己的后端已经有一套返回格式那就改 request.js 里的分发逻辑把登录失效统一处理成清 token 并跳登录页。核心原则是前端只认一个承诺任何接口在 data 里给到页面需要的数据错误走统一提示。6.3 验证流程与我的习惯改完任何一个模块不要只点编译看模拟器。我会在开发者工具里先“清缓存 → 全部清除”冷启动之后按“登录 → 首页 → 详情 → 加购 → 结算 → 下单”的顺序在 Network 面板过一遍接口。这个链路里只要有一个环节报错就说明改动影响到了公共逻辑。从那以后我每次拿到商城源码先跑通一遍链路再动代码改完再强制完整跑一遍闭环。跑通了才敢说这个改造是立得住的。希望帮到你。本文还有配套的精品资源点击获取