写在前面
LikeShop 单商户 SaaS 版的核心诉求是“无限多开”——一套代码部署一次,可以为多个客户开通独立的商城账号,每个租户拥有独立的域名或子域名、独立的会员体系和订单数据。这和单商户标准版“一个客户一套系统”的模式有本质区别,打包时踩的坑也完全不同。
这篇文章聚焦 SaaS 版在实际多端打包中最容易翻车的几个环节,从环境变量到多租户域名适配,再到租户级参数隔离,把整个流程拆开讲清楚。
一、动手之前:SaaS 版环境变量配置的两个特殊性
不管打包哪个端,SaaS 版的环境变量配置都是第一步,但比标准版多两个需要注意的地方。
打开 uniapp/ 目录下的 .env.production.example,创建副本去掉 .example 后缀。关键配置项是服务端接口地址:
VITE_APP_BASE_URL='https://你的服务端域名'
第一,绝对不能填 localhost。 官方文档特别强调过这个问题,因为 uni-app 在多端运行时对本地地址的解析逻辑不一致。本地开发时也要用 hosts 映射到一个可访问的域名。
第二,SaaS 版的域名配置需要指向租户的三级域名。 根据 LikeShop 官方文档,单商户 SaaS 版编译小程序时,config/app.js 中的域名需指向“平台新增商城时生成的三级域名”,而不是平台主域名。这是 SaaS 版和标准版在配置层面最核心的区别——标准版只有一个域名,SaaS 版每个租户都有自己的访问入口。
二、多租户域名适配:H5 打包最大的坑
SaaS 版 H5 打包操作本身和标准版一样——HBuilderX 中“发行”→“网站-PC Web或手机H5”,编译完成后替换服务端的 mobile 目录。但真正的难点在打包之后。
域名识别与 H5 基础路径
SaaS 版通常支持三种租户识别方式:独立域名、子域名、请求参数。这意味着同一份 H5 代码可能被部署到 shop-a.com、shop-b.com 或者 a.platform.com 等多个域名下。
在 manifest.json 的 h5 节点中配置 router.base 时,不能写死某个租户的路径前缀。如果你的 SaaS 平台采用的是子域名方案(a.platform.com、b.platform.com),基础路径可以统一设为 /;如果是独立域名方案,每个租户的 Nginx 配置都需要单独设置反向代理指向同一份编译产物。
跨域与 CORS
开发阶段你可能在 h5.devServer.proxy 里配了代理,但打包之后代理完全不生效。SaaS 版尤其要注意:租户 A 的 H5 请求 a.platform.com/api/xxx,租户 B 请求 b.platform.com/api/xxx,后端的 CORS 白名单需要动态匹配所有租户域名。建议在 Nginx 层统一配置反向代理,避免在每个租户域名的 CORS 白名单上逐个添加。
rpx 的陷阱
rpx 在小程序和 App 端表现正常,但在 H5 端(尤其是 PC 浏览器访问时)可能被放大得离谱。建议在 pages.json 的 globalStyle 中限制最大宽度,避免布局在高分辨率屏幕上崩掉。
三、微信小程序打包:分包与 SaaS 特有的参数隔离
分包配置
LikeShop 的页面数量不少,微信小程序主包限制 2MB,直接打包大概率超体积。在 pages.json 中通过 subPackages 配置分包,主包只保留首页、TabBar 页面和公共资源。分包内的页面不能作为 TabBar 页,跨分包引用资源时绝对路径可能在小程序运行时出错。
SaaS 版的参数隔离问题
这是 SaaS 版小程序打包和标准版最大的不同。标准版只需要在 manifest.json 的 mp-weixin.appid 中填入一个小程序 AppID。但 SaaS 版可能面临两种情况:
方案一:每个租户独立小程序。 每个租户在微信公众平台注册自己的小程序,使用自己的 AppID。这种情况下,manifest.json 中的 AppID 需要在打包前替换,或者通过构建脚本自动注入。建议引入配置驱动的动态构建方案,把 AppID、接口地址等参数抽离到构建配置中,而不是手动修改源码。
方案二:平台统一小程序,通过域名区分租户。 部分 SaaS 平台选择用一个主小程序服务所有租户,租户的识别通过小程序内嵌 H5 的域名切换实现。这种方案下,小程序的 manifest.json 配置不变,但 H5 部分的域名适配就需要用到前面提到的多域名方案。
无论哪种方案,新增页面必须先在 pages.json 中注册路由,否则页面在小程序中无法被访问。
四、App 端打包:SaaS 场景下的参数隔离
App 端打包的核心流程和标准版一致,但 SaaS 版有一个容易被忽略的问题:微信登录和支付的参数是租户级的还是平台级的?
如果每个租户有自己的微信开放平台账号,那么 App 中微信登录的 AppID 和 AppSecret 就是租户级的,不能写死在 manifest.json 中。一种可行的方案是:App 中只集成一套微信登录 SDK,租户参数通过接口动态获取,在登录时根据当前租户的配置来初始化。但这需要在 App 端额外处理参数注入逻辑,比标准版复杂不少。
如果你选择的是平台统一账号的方案(平台申请一个微信开放平台账号,所有租户共用),那么 manifest.json 中的参数配置和标准版一致,但登录后获取的用户 openid 需要结合 tenant_id 来识别用户归属——同一个微信用户在租户 A 和租户 B 的商城中是不同账号。
另外,App 端如果用到摄像头、相册、定位等权限,必须在 manifest.json 的 distribute.android.permissions 中显式声明,否则功能直接失效。
五、条件编译:SaaS 版多端差异处理的最佳实践
在 LikeShop 的代码中,你会看到大量条件编译的写法:
// #ifdef MP-WEIXIN
// 微信小程序专属逻辑
// #endif
// #ifdef H5
// H5 专属逻辑
// #endif
SaaS 版二开时有一个特别容易犯的错误:用 if/else 判断平台,而不是用条件编译。这样写的后果是,所有平台的代码都会被完整打包进每一个端,不仅增大包体积,还可能导致不相关的逻辑在错误的平台上执行。
但 SaaS 版还有一个额外的注意点:租户级的差异不要用条件编译来处理。条件编译处理的是“端差异”(H5 vs 小程序 vs App),租户差异应该通过运行时接口或环境变量来处理。把租户差异写进条件编译,会导致代码在编译时被裁剪,而租户是在运行时才能确定的。
六、打包前自查清单
| 检查项 | SaaS 版特别注意 |
|---|---|
| 环境变量域名 | 是否指向租户的三级域名而非平台主域名 |
| H5 基础路径 | 多域名部署时是否兼容子域名/独立域名 |
| 小程序 AppID | 是租户独立还是平台统一?构建配置是否支持参数注入 |
| 微信开放平台参数 | App 端微信登录/支付是租户级还是平台级 |
| 条件编译 | 租户差异是否错误地写进了条件编译 |
| 分包配置 | 主包是否控制在 2MB 以内 |
| App 权限声明 | 摄像头、相册、定位等权限是否已在 manifest.json 中声明 |
LikeShop 单商户 SaaS 版的多端打包,难的不是编译操作本身,而是“一套代码、多个租户、多个端”的矩阵下,域名、AppID、支付参数等租户级配置的正确隔离与注入。建议在正式打包发布之前,先按上面的清单逐项自查,尤其是环境变量域名、小程序 AppID 的租户归属、以及 H5 多域名适配这三处,基本能覆盖 SaaS 版打包中 80% 的翻车场景。