FEATURED · 精选文章

uniapp多平台打包配置:一套代码实现多地区多环境自动化构建

发布时间 / 2026/9/19 4:02:19
来源 / 创域科博编辑部
栏目 / 资讯中心
uniapp多平台打包配置:一套代码实现多地区多环境自动化构建 1. 项目整体设计与思路拆解1.1 这个项目到底在解决什么问题先说结论uniapp 项目的打包配置难的不是“能不能打”而是“一套代码怎么在十几个目标环境下各自长出正确的样子”。我接手这个项目的时候团队已经有了一套能跑的 uniapp 工程但打包方式基本靠人肉记忆发测试包时手动改接口地址发生产包时再改回来发微信小程序时手动改 appid发 App 时又得重新配置证书。每次发版少则半小时多则一下午而且经常出现“测试环境包打成了生产环境”这种让测试同学直接崩溃的乌龙。所以这个项目的核心目标就一句话把打包这件事从“人工操作”变成“配置驱动”。具体拆解下来要解决的问题有三个多地区同一个 App在国内和海外比如东南亚、拉美需要不同的域名、不同的支付渠道、不同的隐私政策弹窗文案甚至不同的时区处理。这些都是写死在代码里就会被骂死的东西。多平台uniapp 官方支持 AppiOS/Android、H5、微信小程序、支付宝小程序、百度小程序、抖音小程序等。每个平台都有自己的配置规范比如小程序的 appid、App 的包名和证书、H5 的域名白名单。多环境开发环境dev、测试环境test、预发布环境staging、生产环境prod。不同环境下接口地址、日志开关、埋点上报地址、甚至 App 的名称都可以不一样。这三个维度叠加起来组合数是 4 个环境 × 6 个平台 × 3 个地区一共 72 种打包组合。如果每种组合都手动改配置不仅效率低还一定会出错。这不是执行力问题是管理问题。1.2 为什么选择 uniapp 而不是原生开发这个项目选 uniapp 作为技术底座不是因为它完美而是因为它能满足业务需求的同时把多平台成本控制在一个可接受的范围内。我的选型理由是这样的一套代码多端复用业务团队只需要维护一套 Vue 语法的代码就能编译到微信小程序、H5、App。虽然各大平台还有差异但足以覆盖 90% 以上非核心业务的页面。平台能力封装完整uniapp 的uni.login、uni.request、uni.getLocation等 API 已经做了跨平台封装开发者不需要关心底层的差异化实现这在多平台打包时能省下大把的时间。生态成熟社区活跃插件市场里有大量开箱即用的组件和 SDK 集成方案比如支付、推送、分享等。这一点在打包环节直接受益——很多平台配置问题社区里都踩过坑了解决方案现成。当然选 uniapp 也有代价。比如它的 App 端本质上是 WebView 原生桥接性能和原生开发没法比再比如一些小众平台的兼容性问题代码里要写不少条件编译。但这些代价相对于“多地区多平台多环境”的业务诉求来说是划算的。1.3 打包方案的整体架构整个方案的核心思路是把原来散落在代码里的硬编码配置全部抽离成“外部配置文件 构建脚本自动注入”。架构上分三层配置层env.config.js / manifest.json ↓ 构建层package.json scripts Node 预编译脚本 ↓ 产物层各平台可发布的包配置层负责声明“所有可能的变量组合”构建层负责根据你传入的参数把这些变量组合注入到对应的文件和配置项里产物层就是最终交给测试、上架、发布的文件。这套方案的优点在于它不改变 uniapp 官方的工程结构而是用工程化手段去自动化那些原本需要手动改的东西。换句话说团队里的任何人都能通过一条命令打出正确的包而不需要记住“这次测试包要配哪个域名”。1.4 你需要准备哪些东西动手之前先把环境准备好。这里列一个清单缺了哪样都会在某个环节卡住Node.js 环境建议 v16 或 v18太老或太新都可能跟 HBuilderX 的 CLI 有兼容问题HBuilderXuniapp 官方 IDE打包 App 时必须用它提供的打包能力微信开发者工具调试和上传微信小程序时需要Android Studio做原生 App 离线打包时使用如果只用云打包就不需要各平台账号微信小程序 appid、支付宝小程序 appid、Apple Developer 账号等另外强烈建议整个团队统一 Node 版本用项目内的.nvmrc或直接锁死 package.json 里的 engines 字段。我在项目里至少遇到三次因为 Node 版本不一致导致的打包脚本行为不同的问题。2. 核心细节解析与实操要点2.1 manifest.json 的配置逻辑manifest.json是 uniapp 项目的“总入口”也是多平台打包时必须重点处理的地方。它包含了应用名称、版本号、appid、图标、权限声明、SDK 配置等关键信息。多平台打包时最容易踩的坑是不同平台的 appid 写错位置。比如微信小程序的 appid 要填在mp-weixin.appid下而支付宝小程序的 appid 要填在mp-alipay.appid下这两者完全不能混。以我们的项目为例一个典型的多平台 manifest.json 配置大致长这样{ name: 跨境物流助手, appid: , description: 多地区多平台物流管理应用, versionName: 1.2.0, versionCode: 120, transformPx: false, app-plus: { usingComponents: true, nvueStyleCompiler: uni-app, compilerVersion: 3, splashscreen: { alwaysShowBeforeRender: true, waiting: true, autoclose: true, delay: 0 }, modules: { Payment: {}, Push: {}, Share: {} }, distribute: { android: { permissions: [ uses-permission android:name\android.permission.CAMERA\/, uses-permission android:name\android.permission.RECORD_AUDIO\/ ], abiFilters: [armeabi-v7a, arm64-v8a] }, ios: { privacyDescription: { NSCameraUsageDescription: 需要使用相机拍摄运输单据, NSLocationWhenInUseUsageDescription: 需要获取位置以定位物流节点 } } } }, mp-weixin: { appid: wx1234567890, setting: { urlCheck: false, es6: true, minified: true }, usingComponents: true }, mp-alipay: { appid: 2021003112345678, setting: { urlCheck: false } }, h5: { title: 跨境物流助手, router: { mode: history, base: / } } }有几点值得单独说明appid字段在 uni-app 中是“DCloud appid”不是微信或支付宝的 appid。如果你只有一个平台可以直接在 HBuilderX 里点“重新获取”但多平台情况下DCloud appid 只影响 uni 统计和原生插件云打包跟微信小程序的 appid 没有关系不要混淆。App 的权限声明permissions和 iOS 的隐私描述privacyDescription必须在打包前确认是否齐全。比如 iOS 如果声明了CAMERA权限却在没有填写NSCameraUsageDescription提交 App Store 审核时会被直接拒掉。H5 的 router 模式如果部署环境需要刷新后保持路由一般用history模式但要注意服务端需要配置 fallback如果用hash模式则没有这个困扰但 URL 上会带一个#部分场景下不够美观。2.2 环境变量与条件编译的正确打开方式uniapp 有一套自己的条件编译机制#ifdef和#ifndef。这是 uniapp 的核心能力也是多平台差异代码的标准写法。条件编译可以作用在三个层级平台级区分APP-PLUS、H5、MP-WEIXIN、MP-ALIPAY等自定义条件可以在package.json里定义自定义条件配合打包命令使用文件级可以通过目录命名-app、-h5、-mp-weixin来让某些文件只在特定平台生效我强烈建议不要在业务代码里大规模使用条件编译。因为条件编译的代码难读难测尤其是当你在一个文件里写了三四种平台的逻辑时后续维护的人大概率会崩溃。一个比较优雅的做法是把平台差异完全隔离在独立的工具文件里业务代码只调用统一的接口。比如// api/platform.js // #ifdef H5 import { h5Login } from ./login-h5.js // #endif // #ifdef MP-WEIXIN import { weixinLogin } from ./login-weixin.js // #endif export function login() { // #ifdef H5 return h5Login() // #endif // #ifdef MP-WEIXIN return weixinLogin() // #endif }这样的话业务代码里只有一行login()平台差异被封装在独立的模块里。测试也只需要按平台分别验证登录这一个子模块而不是去翻整个业务链路。2.3 多地区配置的集中管理多地区支持是这个项目比较有特色的地方。因为不同地区的法律法规、网络环境、运营策略不同App 在打包时就需要带上不同的地域配置。我们的做法是创建一个env.config.js文件统一维护所有地区的配置项// config/env.config.js module.exports { // 国内环境 cn: { baseUrl: https://api.cn.example.com, apiVersion: v2, h5Domain: https://h5.cn.example.com, currency: CNY, timezone: Asia/Shanghai, privacyTitle: 中国地区隐私政策, payChannel: [wechat, alipay], umengAppKey: cn_umeng_key, buglyAppId: cn_bugly_id }, // 东南亚环境越南、泰国等 sea: { baseUrl: https://api.sea.example.com, apiVersion: v2, h5Domain: https://h5.sea.example.com, currency: VND, timezone: Asia/Ho_Chi_Minh, privacyTitle: Southeast Asia Privacy Policy, payChannel: [stripe, local_wallet], umengAppKey: sea_umeng_key, buglyAppId: sea_bugly_id }, // 拉美环境巴西、墨西哥等 latam: { baseUrl: https://api.latam.example.com, apiVersion: v3, h5Domain: https://h5.latam.example.com, currency: BRL, timezone: America/Sao_Paulo, privacyTitle: Política de Privacidade, payChannel: [pix, mercadopago], umengAppKey: latam_umeng_key, buglyAppId: latam_bugly_id } }然后在项目里通过一个统一的getEnvConfig()来读取当前环境的配置// utils/config.js import envConfig from /config/env.config.js let env cn // #ifdef APP-PLUS env uni.getStorageSync(currentRegion) || cn // #endif // #ifdef H5 const urlParams new URLSearchParams(window.location.search) env urlParams.get(region) || cn // #endif export const ENV env export const CONFIG envConfig[env] || envConfig.cn为什么不用“一份代码 运行时动态切换地区”而是“打包时注入地区”原因有两个部分配置无法在运行时更改。比如安卓的包名applicationId一旦发布就无法修改不同地区如果用同一个包名上不同的应用商店会被视为同一应用容易出问题。还有 iOS 的 Bundle ID 也是同理。运营和合规的要求。不同地区在应用商店上架时往往需要不同的隐私政策链接、联系方式、甚至不同的应用名称和图标。这些只能在打包时分别设置运行时改不了。因此多地区支持的本质就是把“地区”作为一个打包参数拉入构建流程。2.4 多环境配置的管理方式环境管理和地区管理类似但维度不同。地区是横向切分环境是纵向切分。我们的环境分四个dev、test、staging、prod。环境配置放在另一个文件里// config/env.env.js module.exports { dev: { baseUrl: https://dev-api.example.com, debug: true, uploadUrl: https://dev-upload.example.com, mock: true, sentryDsn: }, test: { baseUrl: https://test-api.example.com, debug: true, uploadUrl: https://test-upload.example.com, mock: false, sentryDsn: https://test.sentry.io/xxx }, staging: { baseUrl: https://staging-api.example.com, debug: false, uploadUrl: https://staging-upload.example.com, mock: false, sentryDsn: https://sentry.example.com/staging }, prod: { baseUrl: https://api.example.com, debug: false, uploadUrl: https://upload.example.com, mock: false, sentryDsn: https://sentry.example.com/prod } }环境配置的读取方式比较简单因为 uniapp 的process.env.NODE_ENV在 HBuilderX 打包时并不总是可靠的。更好的做法是“打包时注入环境变量运行时通过全局对象读取”。这里我选择通过definePlugin的方式在构建时注入const webpack require(webpack); const envConfig require(./config/env.env.js); const regionConfig require(./config/env.config.js); const currentEnv process.env.UNI_ENV || dev; const currentRegion process.env.UNI_REGION || cn; module.exports { configureWebpack: { plugins: [ new webpack.DefinePlugin({ __ENV__: JSON.stringify(currentEnv), __REGION__: JSON.stringify(currentRegion), __CONFIG__: JSON.stringify({ ...envConfig[currentEnv], ...regionConfig[currentRegion] }) }) ] } }在业务代码里直接console.log(__ENV__)就能读到当前环境名不需要再走运行时检测逻辑。2.5 如何把配置注入到 manifest.jsonmanifest.json的自动化修改是打包方案里比较关键的一环。因为 uniapp 的 CLI 打包模式区别于 HBuilderX 可视化界面打包在编译时会把manifest.json里的一部分内容写入最终的代码包中所以在打包之前我们需要先把manifest.json里的关键字段动态覆盖成当前构建的目标值。我写了一个 Node 脚本scripts/build-config.js来处理这件事const fs require(fs) const path require(path) const manifestPath path.resolve(__dirname, ../src/manifest.json) const manifest JSON.parse(fs.readFileSync(manifestPath, utf-8)) function updateManifest(config) { // 更新基础信息 manifest.name config.appName manifest.versionName config.versionName manifest.versionCode config.versionCode // 更新 H5 配置 if (config.h5 config.h5.title) { manifest.h5.title config.h5.title } // 更新微信小程序 appid if (config[mp-weixin] config[mp-weixin].appid) { manifest[mp-weixin].appid config[mp-weixin].appid } // 更新支付宝小程序 appid if (config[mp-alipay] config[mp-alipay].appid) { manifest[mp-alipay].appid config[mp-alipay].appid } // 更新 App 端 if (config[app-plus]) { manifest[app-plus].distribute { ...manifest[app-plus].distribute, ...config[app-plus].distribute } } fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)) console.log([build-config] manifest.json updated for env${config.env}, region${config.region}) } // 命令行参数示例node scripts/build-config.js --envprod --regioncn const args process.argv.slice(2) const getArg (key) { const item args.find(a a.startsWith(--${key})) return item ? item.split()[1] : null } const env getArg(env) || dev const region getArg(region) || cn const unifiedConfig require(../config/unified.config.js) updateManifest(unifiedConfig[env][region])注意这个脚本需要在 HBuilderX 的cli命令执行之前运行否则 manifest.json 的修改不会生效。实际调用顺序是node scripts/build-config.js --envprod --regioncn npx uni build -p mp-weixin2.6 Package.json 脚本编排为了让团队成员不需要记长命令我把所有打包动作收敛到package.json的scripts里{ scripts: { build:mp-weixin:test: node scripts/build-config.js --envtest --regioncn npx uni build -p mp-weixin, build:mp-weixin:prod: node scripts/build-config.js --envprod --regioncn npx uni build -p mp-weixin, build:h5:dev: node scripts/build-config.js --envdev --regioncn npx uni build -p h5, build:h5:prod: node scripts/build-config.js --envprod --regioncn npx uni build -p h5, build:app:test: node scripts/build-config.js --envtest --regioncn npx uni build -p app, build:app:prod:sea: node scripts/build-config.js --envprod --regionsea npx uni build -p app } }这样同事只需要执行npm run build:mp-weixin:test就能打出一个微信小程序测试包。命令的名称本身就是文档团队成员不需要去理解内部机制。3. 实操过程与核心环节实现3.1 多环境自动切换接口地址的实现这是打包方案里最重要的一环。如果没有做这一步那么上面所有的配置管理都只是“静态文件管理”业务代码依然写死接口地址一切白费。我们的做法是把接口请求封装在一个统一模块里模块内部根据注入的__CONFIG__对象来选择 baseUrl。// utils/request.js import { CONFIG } from ./config const BASE_URL CONFIG.baseUrl const API_VERSION CONFIG.apiVersion || v1 export function request(options) { return new Promise((resolve, reject) { uni.request({ url: ${BASE_URL}/${API_VERSION}${options.url}, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, X-Region: CONFIG.region || cn, Authorization: uni.getStorageSync(token) || }, success: (res) { if (res.statusCode 200) { resolve(res.data) } else if (res.statusCode 401) { uni.navigateTo({ url: /pages/login/login }) reject(res) } else { reject(res) } }, fail: (err) reject(err) }) }) }注意这里 URL 的拼接方式${BASE_URL}/${API_VERSION}${options.url}。之所以把 API 版本单独拿出来是因为不同环境、不同地区的 API 版本可能不一致。比如国内环境可能还在用 v1 接口海外环境已经上了 v2。把版本号抽离出来就能用同一个配置中心同时控制。3.2 微信小程序多平台打包实操微信小程序是我们团队发布频率最高的平台。其打包分为两步先出构建产物再通过微信开发者工具上传。第一步执行打包命令npm run build:mp-weixin:test构建完成后产物在dist/build/mp-weixin目录下。此时打开微信开发者工具选择“导入项目”目录选中该文件夹填入测试环境的微信小程序 appid即可预览调试。第二步上传体验版或正式版。微信开发者工具右上角“上传”按钮版本号可以填1.2.0备注填“测试环境-0921-修复登录问题”。这样团队内部就能通过“体验版”二维码进行测试。这里有一个实操中的重要细节微信小程序的urlCheck合法域名校验。uniapp 在构建时manifest.json里的mp-weixin.setting.urlCheck字段只影响编译期真正的运行期域名校验是在微信公众平台后台配置的 request 合法域名。如果测试环境的域名没有加到白名单开发工具里可以临时勾选“不校验合法域名”但真机预览时这个选项不稳定测试同学会反复遇到“不在以下 request 合法域名列表中”的问题。解决办法是测试环境的域名尽早加入微信公众平台的白名单。如果确实不能加入比如每个开发者都有自己的本地 dev 环境我建议让开发者各自通过微信开发者工具的“本地设置”里勾选不校验合法域名来调试但发给测试的体验版一定要确保白名单配置正确。3.3 App 云打包实操App 端的打包uniapp 官方提供两种方式云打包和本地打包。云打包适合团队没有原生开发能力、只需要快速出包的情况。你只需要在 HBuilderX 或 CLI 里配置好证书信息云端就会帮你生成 apk/ipa。云打包的优点是快、省事缺点是不可控尤其当你的原生插件或第三方 SDK 和云端环境有兼容问题时排查起来比较头疼。本地打包则要求你准备 Android Studio或 Xcode使用官方提供的 SDK 工程把 uniapp 编译后的app-resources资源包放进工程里再用原生工具链打包。本地打包适合需要集成原生代码、或者对构建产物有精细控制需求的场景。我个人的建议是如果团队里没有原生开发工程师优先用云打包如果 App 需要用到大量自定义原生插件就必须走本地打包。我们项目因为要集成多家支付 SDK走了本地打包路线。本地打包的大致流程如下在 HBuilderX 的“本地打包”界面生成app-resources资源包或者用 CLI 生成。把这些资源复制到 Android Studio 工程的assets/apps/{appid}/www目录下。修改 Android 工程里的dcloud_control.xml配置确保 appid 跟资源包一致。修改 AndroidManifest.xml 里的包名、权限声明、以及各 SDK 的 key。用 Gradle 构建出正式签名的 APK。3.4 H5 多环境打包实操H5 是最简单的平台但有一个坑必须提base 路径和静态资源路径。uniapp 构建 H5 时默认资源引用路径是绝对路径/static/...只有当部署到域名根目录时才没问题。如果部署到子路径比如https://example.com/h5/cn/就需要在manifest.json里设置h5.router.base。{ h5: { router: { base: /h5/cn/ } } }这个路径也可以由构建脚本自动注入。另外还有一点H5 运行时的环境变量只能通过 URL 参数来区分比如https://example.com/h5/cn/?envtest。我们的做法是在入口文件main.js里读取 URL 参数决定使用哪套环境配置// main.js const params new URLSearchParams(window.location.search) const env params.get(env) || prod // 动态加载对应环境的配置这样做的好处是同一套静态资源可以部署到 CDN然后通过不同的 URL 参数实现环境切换而不需要为每个环境单独构建一份 H5。缺点是 URL 里带参数会稍微有点丑而且如果服务端缓存了带参数和不带参数两个版本需要确认缓存策略。3.5 多地区国际化配置实战多地区打包必然涉及到国际化i18n问题。uniapp 官方推荐使用vue-i18n但实际使用中发现几个需要注意的地方。首先是语言包的加载方式。不要把所有语言包都打进去这样会让代码包体积膨胀。更好的做法是根据地区配置只加载对应的语言包。// utils/i18n.js import { createI18n } from vue-i18n import { CONFIG } from ./config const messages { zh: require(/locales/zh-CN.js), en: require(/locales/en-US.js), vi: require(/locales/vi-VN.js), pt: require(/locales/pt-BR.js) } const i18n createI18n({ legacy: false, locale: CONFIG.locale || zh, fallbackLocale: zh, messages }) export default i18n然后是日期的格式化。不同地区对日期和时间的展示习惯不同比如中国习惯2025-03-18 14:30美国习惯03/18/2025 2:30 PM巴西习惯18/03/2025 14:30。这些最好不要在组件里一个个处理而是封装一个formatDate方法通过Intl.DateTimeFormat来自动适配地区export function formatDate(timestamp, style medium) { const localeMap { cn: zh-CN, sea: en-SG, latam: pt-BR } return new Intl.DateTimeFormat(localeMap[CONFIG.region] || zh-CN, { dateStyle: style, timeStyle: style full ? long : short }).format(timestamp) }3.6 多平台多渠道账号体系打通多地区多平台发布时账号体系也需要特殊处理。比如国内版 App 需要支持微信登录和手机号一键登录东南亚版需要支持 Google 登录拉美版需要支持 Facebook 登录。不同平台登录方式完全不一样。我的经验是登录方式根据地区动态渲染而不是在代码里写死。template view button v-formethod in loginMethods :keymethod.type clickhandleLogin(method.type) {{ method.label }} /button /view /template script setup import { CONFIG } from /utils/config const loginMethods computed(() { const methods [] if (CONFIG.loginMethods.includes(wechat)) { methods.push({ type: wechat, label: 微信登录 }) } if (CONFIG.loginMethods.includes(google)) { methods.push({ type: google, label: Google 登录 }) } if (CONFIG.loginMethods.includes(phone)) { methods.push({ type: phone, label: 手机号登录 }) } return methods }) /script这里的loginMethods在env.config.js的每个地区配置里维护。这样做的好处是运营同学想调整某地区的登录方式时只需要改配置文件不需要发版打包脚本自动生效。4. 常见问题与排查技巧实录4.1 微信小程序开发工具加载分包内容为空这个问题我们遇到过两三次现象是微信开发者工具打开项目后首页能显示但跳转分包页面时页面空白控制台报module is not defined。排查思路先看dist/build/mp-weixin目录下是否真的生成了分包的 js 文件。如果分包目录下只有 wxml 和 wxss没有 js 文件说明构建时分包逻辑崩了。检查pages.json里的subPackages配置。路径写错是常见原因。还有一个隐蔽原因代码里某个 js 文件同时被主包和分包引用导致 webpack 打包时把模块放错了位置。解决方案把分包的公共代码提取到主包目录下确保分包内的 js 没有外部依赖。如果必须共用可以考虑用uni.$emit/uni.$on做事件通信把跨包的函数调用改为事件机制。4.2 App 云打包时manifest.json 修改不生效怎么办这里有一个容易误操作的点云打包的 appidDCloud appid和 manifest 中的 appid 是两回事。如果你在打包时改动过 DCloud 的 appid云打包平台记录的 appid 和本地资源包的 appid 不一致打包会失败或者打出来的包运行时报错。解决办法是在 HBuilderX 中固定项目的 DCloud appid不要在生产环境变动。如果需要本地打包确保dcloud_control.xml里的 appid 与项目中保持一致。4.3 微信小程序真机预览时请求一直被拦大多数人第一反应是去微信公众平台检查“request 合法域名”。但我遇到过一个情况是开发工具正常预览版正常正式版完全没反应。后来才发现原因是我们在manifest.json里设置了mp-weixin.setting.urlCheck: false导致开发工具和预览版都不拦截但正式版提交审核后发布的版本不受这个字段影响依然强制校验域名。而我们的一个接口域名预发布环境没有加入白名单所以正式版里这个接口静默失败。经验教训决不能依赖urlCheck: false做敷衍的调试所有环境、所有域名必须走正规的白名单配置。可以用“x-switch”方式在 Alpha 版本里用 debug 配置但正式版一定要面向真实域名。4.4 不同地区打同一个包微信小程序 appid 被占用我们曾经尝试用一个微信小程序 appid通过代码里动态切换openid指向不同的地区服务端。结果上线后发现微信的wx.login拿到的code只能换到当前 appid 对应的openid而我们的服务端逻辑是根据openid识别用户的导致所有地区都识别成国内用户海外用户的身份完全错乱。解决方案很直接一个地区一个微信小程序 appid不要共用打包时通过manifest.json的mp-weixin.appid字段注入不同地区的 appid。这样每个地区的小程序是独立的账号体系服务端也更好处理。4.5 安卓包名与 iOS Bundle ID 的规划这部分踩过的坑让我总结出一条铁律包名和 Bundle ID 在项目启动时就要做整体规划后面几乎不能改。安卓包名被应用商店、第三方 SDK、推送服务等多方引用一个 appid 变更意味着所有集成的 SDK 都需要重新申请 key 并验证。最终我们采用如下命名规则国内版com.example.logistics.cn东南亚版com.example.logistics.sea拉美版com.example.logistics.latamiOS Bundle ID 同样遵循这个规则。这样不同地区之间完全隔离互不影响。4.6 环境配置错误导致的线上生产事故案例最后分享一个我们真实踩过的坑这是整个打包方案从“能用”到“好用”的转折点。某次发版同事在用build:mp-weixin:prod时因为命令敲得太快没注意参数实际执行的是build:mp-weixin:test。结果是线上小程序看着一切正常但所有接口请求都打到了测试环境用户数据全部错乱。客服电话被打爆。事后复盘问题出在那个脚本只是“约定”没有“拦截”。所以我们现在在打包脚本里加了一层保护生产环境打包时要求手动输入确认码。// scripts/build-config.js const readline require(readline) function confirmProduction() { return new Promise((resolve) { if (env ! prod) { resolve(true) return } const rl readline.createInterface({ input: process.stdin, output: process.stdout }) rl.question(请确认当前是生产环境打包输入 YES 继续: , (answer) { rl.close() resolve(answer.trim().toUpperCase() YES) }) }) } // 使用 confirmProduction().then((ok) { if (!ok) { console.log([build-config] 已取消生产环境打包) process.exit(1) } updateManifest() })这算是最简单的一层保险但效果立竿见影。后来我们在 CI/CD 流程里也加了同样的校验确保只有带特定 tag 的提交才能触发生产环境构建。4.7 常见问题速查表问题现象可能原因解决方案小程序预览时请求失败域名未加入白名单登录微信公众平台在“开发管理-服务器域名”中添加合法域名App 打包后无法获取定位缺少定位权限检查 manifest.json 中 app-plus.distribute.android.permissions 和 iOS privacyDescriptionH5 部署在子目录后样式丢失router base 未设置在 manifest.json 中设置 h5.router.base 为实际部署路径云打包提示 appid 不存在DCloud appid 不一致用 HBuilderX 打开项目确认 appid 和 cloud 打包平台一致多地区版本包混合打包参数错误在打包脚本中加入生产环境确认机制并将地区参数写进脚本名iOS 提交审核被拒隐私描述缺失在 manifest.json 中补充 NS*UsageDescription 字段小程序分包跳转空白分包公共代码冲突将公共代码提取至主包分包内只保留独立业务代码5. 扩展思考与运维建议5.1 如何把打包方案接入 CI/CD在手工脚本跑通之后下一步就是接流水线。我们的实践是在 GitLab CI 里配置三个 job分别对应微信小程序、AppAndroid、H5。build-mp-weixin: stage: build script: - npm ci - node scripts/build-config.js --envprod --regioncn - npx uni build -p mp-weixin artifacts: paths: - dist/build/mp-weixin only: - tagsApp 端因为是本地打包需要在 CI 机器上提前装好 Android SDK、Gradle 等环境然后把构建产物的 apk 上传到内部制品库或者 OSS。接入 CI 后有个很明显的好处每个人都能看到构建日志。以前手工打包出问题全靠当事人回忆自己执行了什么命令现在整个构建过程都有日志留存问题定位时间大幅缩短。5.2 多地区版本的发布协同策略多地区版本同时发布时节奏很难完全同步。比如国内版微信小程序审核快可能今天就过东南亚版的 Google Play 审核可能需要一周。我的建议是每个地区的每个平台都做独立的版本号管理不要用同一个版本号硬撑。可以在 CI 产物里自动打上地区标识比如产物的文件名用logistics-cn-mp-weixin-v1.2.0这种格式避免人工拿错包也方便后续追溯。5.3 后续还可以扩展的方向这个打包方案跑通后我还能看到几个明显的扩展空间灰度发布支持在配置层加一个grayPercent字段打包时生成带灰度标识的包服务端根据这个标识决定把哪些用户导流到新版本。多套签名管理不同地区使用不同的签名证书。目前我们是用脚本配置路径后续可以做成密钥管理系统把证书放进去构建时自动拉取对应地区证书。自动生成各平台渠道包安卓市场上每个渠道都有不同的统计标识可以扩展配置层把渠道号也作为打包参数。供应链级的版本联动后端、H5、App 三个端协同发版时可以做一个统一发版平台调用各端的构建接口一次性触发多个产物的构建。这些扩展的方向仍然是基于“配置驱动 自动化构建”的底层思路只要配置层的结构设计得足够清晰后续的扩展都只是加字段和加脚本的问题。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻