简介:萤火商城 v2.0.8 多端版是一套面向中小型电商创业者、独立开发者与二次开发学习者的轻量级开源商城系统,基于 PHP7.4 + ThinkPHP6.0 + Uni-APP + Ant Design Vue 构建,前后端源码完全开源,支持微信小程序、H5、公众号与 APP 多端运行,可快速搭建个性化独立商城,兼顾学习与商用场景。压缩包共约 2000 个文件,整体 17.34MB,以 1175 个 PHP 后端逻辑文件、155 个 JS 脚本、46 个 CSS 样式、32 个 HTML 页面及 136 个 PNG、45 个 GIF 等静态资源为主,另含 SQL 建表脚本、JSON 配置、Markdown 说明与 License 授权文件,目录结构清晰,便于按模块检索与二次开发。目前已有 267 人学习下载。资源提供超管与商户双后台演示入口,读者可借此研究多端商城的前后端分离架构、订单与商品模块组织方式,并对照源码完成个性化功能改造与部署实践。
1. 萤火商城 v2.0.8 多端版:一套代码怎么同时喂饱小程序、H5 和 App
如果你手里只有一套前端代码,却要同时交付微信小程序、H5 和 App 三个端,第一反应大概率是「三套代码并行维护,改一个按钮改三遍」。萤火商城 v2.0.8 多端版这个标题,核心卖点就在这——用 uni-app 的编译时多端能力,把一套 Vue 代码分别产出小程序包、H5 站点和 App 安装包。它面向的是中小型自营电商团队:没有独立前端架构组,但需要快速上线多个流量入口,同时后台要能统一管商品、订单和会员。
这个版本号里的 v2.0.8 属于补丁迭代,多端版 V2.0 是主版本线,商业两个字意味着它默认带了拼团、秒杀、分销、优惠券这些变现模块,而不是一个裸的商城壳。我见过不少团队拿它当起点做二次开发,省掉的是从零搭 SKU、购物车、支付回调这些脏活的时间。但多端不是银弹,编译产物在不同端的表现差异、支付通道的端侧适配、以及商业模块的并发瓶颈,才是真正决定这套东西能不能上生产的关键。下面按「先跑起来、再拆商业模块、最后排坑」的顺序讲透。
2. 把萤火商城 v2.0.8 在本地跑起来:环境、编译与三端产物
2.1 环境选型:为什么是 uni-app + Vue2 而不是原生三端
萤火商城多端版的技术底座是 uni-app,Vue2 语法,配合 HBuilderX 或 CLI 编译。选它的理由很直接:小程序和 H5 的 DOM 模型差异大,原生写就是两套逻辑,而 uni-app 用条件编译把差异收敛到#ifdef块里,业务代码复用率能到 85% 以上。App 端走的是 5+ Runtime 或 uni-app 的 nvue 渲染,性能比纯 WebView 好,但比原生 Flutter 弱,这是取舍。
我一般建议团队先确认三件事再动手:Node 版本锁在 14.x 或 16.x(Vue2 生态对 18+ 的兼容有坑),HBuilderX 用 3.6 以上,小程序开发者工具装稳定版。数据库用 MySQL 5.7 或 8.0,PHP 侧是 ThinkPHP 框架,这是萤火商城后端的老底子。如果你只做前端二次开发,后端可以直接用官方提供的接口文档对接,但商业模块的订单状态机建议自己过一遍。
2.2 拉代码到编译出小程序包的最小命令链
假设你已经拿到源码包,解压后目录结构通常是frontend(uni-app 前端)和backend(PHP 后端)分离。前端部分用 CLI 方式跑,比 HBuilderX 图形界面更容易进 CI。
# 进入前端目录,安装依赖 cd frontend npm install --registry=https://registry.npmmirror.com # 开发模式编译到微信小程序,产物在 dist/dev/mp-weixin npm run dev:mp-weixin # 生产模式编译 H5,产物在 dist/build/h5 npm run build:h5 # 生产模式编译 App 资源,再用 HBuilderX 云打包或本地打包 npm run build:app-plusnpm run dev:mp-weixin会监听文件变化并增量编译,适合开发调试;build系列做压缩和 Tree-shaking,产物更小。编译完成后,小程序产物用微信开发者工具打开dist/dev/mp-weixin目录即可预览。H5 产物直接扔 Nginx 静态目录。App 端需要把dist/build/app-plus导入 HBuilderX 做原生打包,或者用uni-app的离线打包方案接 Android Studio / Xcode。
参数上重点看manifest.json里的appid、mp-weixin.appid和各端router.base。H5 部署到子路径时,router.base必须和 Nginx 的location一致,否则刷新 404。这个坑我后面还会提。
2.3 后端接口联调:域名、跨域与请求封装
前端跑起来后第一件事是改请求基地址。萤火商城前端一般把 baseURL 放在common/config.js或.env文件里。
// common/request.js 里的核心拦截逻辑 const BASE_URL = process.env.NODE_ENV === 'development' ? 'http://127.0.0.1:8080/api' // 本地后端 : 'https://your-domain.com/api'; // 生产域名,必须 HTTPS // 请求拦截:统一带 token uni.addInterceptor('request', { invoke(args) { args.header = args.header || {}; const token = uni.getStorageSync('token'); if (token) args.header['Authorization'] = 'Bearer ' + token; args.url = BASE_URL + args.url; } });这段逻辑说明:开发环境走本地 IP,生产环境走 HTTPS 域名。小程序端强制要求 HTTPS 且域名要在微信后台配置白名单,H5 端则受浏览器同源策略限制,需要后端开 CORS 或 Nginx 反代。App 端相对宽松,但 Android 9+ 默认禁止明文 HTTP,所以生产必须上证书。
参数上注意Authorization头的格式要和后端中间件一致,萤火商城默认用 JWT,过期时间在config/jwt.php里调。联调阶段如果一直 401,先看 token 有没有正确写入 Storage,再看后端密钥是否和前端签名一致。
3. 商业模块拆解:拼团、秒杀、分销在多端下的落地差异
3.1 拼团与秒杀:库存扣减放在哪一层
商业版的核心是营销模块。拼团和秒杀对库存的准确性要求最高,萤火商城的默认实现是「下单减库存」,这在低并发下没问题,但秒杀场景会超卖。常见做法是改成 Redis 预扣减 + 消息队列异步落库。
// 秒杀预扣库存的简化逻辑(ThinkPHP + Redis) public function seckillStock($goodsId, $userId) { $key = 'seckill:stock:' . $goodsId; // Lua 脚本保证原子性:判断库存 > 0 则减 1 $lua = <<<LUA if tonumber(redis.call('get', KEYS[1])) > 0 then redis.call('decr', KEYS[1]) return 1 else return 0 end LUA; $result = Redis::eval($lua, 1, $key); if (!$result) { return ['code' => 0, 'msg' => '库存不足']; } // 写入队列,异步创建订单 Redis::lpush('seckill:order:queue', json_encode([ 'goods_id' => $goodsId, 'user_id' => $userId, 'time' => time() ])); return ['code' => 1, 'msg' => '排队中']; }逻辑说明:Lua 脚本在 Redis 单线程里执行,保证「判断 + 扣减」原子性,避免并发超卖。扣减成功后不直接写 MySQL,而是推入队列由消费者慢慢建单,削峰填谷。参数上seckill:stock的初始值要在活动开始前从 MySQL 同步过来,活动结束后再对账。
多端差异在于:小程序端用户点击秒杀按钮后,如果网络抖动,可能重复提交。前端要做按钮防抖 + 请求幂等 token,后端用user_id + goods_id + 活动ID做唯一索引兜底。
3.2 分销关系的绑定时机与端侧存储
分销模块的坑在绑定关系。萤火商城默认是用户点击分享链接后,在落地页写入pid到本地存储,注册时带上。但小程序和 H5 的存储隔离,App 又不一样。
// 分享落地页统一处理分销绑定 onLoad(options) { // options.pid 来自分享链接参数 if (options.pid) { // 各端统一用 uni.setStorageSync,编译时会映射到端侧存储 uni.setStorageSync('distributor_pid', options.pid); // 同时上报后端,记录访问轨迹 this.$api.post('/distributor/visit', { pid: options.pid, scene: this.getScene() // 区分小程序/H5/App }); } }逻辑说明:uni.setStorageSync在小程序端映射到wx.setStorageSync,H5 端映射到localStorage,App 端映射到 plus 存储。这样业务代码不用改。参数scene用来区分来源端,后端做归因分析时有用。
注意:小程序分享链接的参数长度有限制,pid建议用短码而非长 ID。H5 端如果用户清了缓存,绑定关系会丢,所以注册时后端要再校验一次。
3.3 多端支付通道的适配清单
支付是商业模块最容易翻车的地方。三端走的通道完全不同:
| 端 | 支付通道 | 关键配置 | 常见失败原因 |
|---|---|---|---|
| 微信小程序 | 微信支付 JSAPI | 商户号、APIv3 密钥、证书 | 签名算法不对、回调域名未配 |
| H5 | 微信 H5 支付 / 支付宝 | 场景信息、referer 白名单 | 非微信内打开、域名未备案 |
| App | 微信 App 支付 / 支付宝 | 应用签名、包名绑定 | 签名不一致、Universal Link 未配 |
我一般让团队先把微信支付 V3 的签名调试通,因为它的证书和密钥体系比 V2 复杂。回调地址必须公网可达,本地开发用内网穿透工具临时映射,但生产环境一定要用备案域名。
4. 多端编译避坑:那些让产物跑不起来的配置细节
4.1 条件编译写错导致某端白屏
现象:H5 正常,小程序白屏,控制台报document is not defined。 原因:代码里直接用了document或window,没有包在#ifdef H5里。 解决:所有浏览器专属 API 必须条件编译。
// 错误写法:小程序端会报错 // const width = document.body.clientWidth; // 正确写法 // #ifdef H5 const width = document.body.clientWidth; // #endif // #ifdef MP-WEIXIN const width = uni.getSystemInfoSync().windowWidth; // #endif4.2 H5 刷新 404 与路由模式
现象:H5 首页正常,点进商品详情后刷新页面 404。 原因:uni-app 默认 hash 模式,如果改成 history 模式,Nginx 没配try_files。 解决:要么保持 hash 模式,要么在 Nginx 加try_files $uri $uri/ /index.html;。我一般建议中小项目直接用 hash,省事。
4.3 小程序包体积超限
现象:微信开发者工具提示主包超过 2MB,无法上传。 原因:商业模块的图片、第三方库全打进主包。 解决:用分包加载,把分销、秒杀等非首屏模块拆到子包;图片走 CDN;uni-app的optimization.subPackages开启。
4.4 App 端键盘遮挡输入框
现象:App 里点输入框,键盘弹起遮住表单。 原因:nvue 页面和 vue 页面的键盘处理不同。 解决:在pages.json里给对应页面配app-plus.softinputMode: "adjustResize",或者用uni.onKeyboardHeightChange手动滚动。
4.5 后端接口返回时间戳精度丢失
现象:小程序端订单时间显示 1970 年。 原因:PHP 返回的毫秒时间戳被 JavaScript 当秒解析。 解决:后端统一返回秒级时间戳,或前端做兼容判断。这个坑很隐蔽,因为 H5 端可能正常,小程序端才暴露。
5. 上线前的验证清单与一个提效技巧
上线前我习惯跑一遍「三端一致性检查」:同一商品在三端的价格、库存、优惠券可用状态必须一致。做法是写一个简单的 Node 脚本,分别请求三端的商品详情接口,对比关键字段。
// check-consistency.js 简化示例 const endpoints = { h5: 'https://h5.your-domain.com/api/goods/detail?id=1', mp: 'https://mp.your-domain.com/api/goods/detail?id=1', app: 'https://app.your-domain.com/api/goods/detail?id=1' }; async function check() { const results = {}; for (const [name, url] of Object.entries(endpoints)) { const res = await fetch(url); const data = await res.json(); results[name] = { price: data.data.price, stock: data.data.stock, coupon: data.data.coupon_enabled }; } console.table(results); // 对比逻辑:任一字段不一致就告警 } check();这个脚本能提前发现缓存不一致、库存同步延迟的问题。参数上注意三端接口域名可能不同,但后端应该是同一套数据库,如果结果不一致,优先查缓存层。
另一个提效技巧是善用uni-app的uni.getSystemInfoSync()做端侧埋点,把用户实际使用的端、版本、机型上报,上线后能快速定位是某一端的问题还是全局问题。我吃过亏——有一次秒杀活动只有 App 端失败,查了两小时才发现是 App 的 WebView 内核版本太老,不支持某个 ES6 语法。从那以后,所有新语法上线前都用babel转一遍,并且在三端真机上各点一遍主流程。希望帮到你。
本文还有配套的精品资源,点击获取