一、 核心痛点与背景
最近我司打算重构原有的业务系统 并且打通钉钉生态,以实现更敏捷、更协同的数字化办公体验,考虑到团队的技术栈以Vue 为主和评估研发成本和效率,我决定使用 Uniapp 从 0 到 1 一把梭;
在钉钉小程序开发中,我们通常需要区分开发环境 (Develop) 、体验环境 (Trial) 和正式环境 (Release) 并对应连接不同的服务端基地址;
传统的 Web 开发经验(依赖 process.env.NODE_ENV )在小程序发布链路中经常失效,可能会导致严重的线上事故(如正式版误连测试库);
二、 落地方案
放弃不稳定的构建变量,采用 "设备特征拦截 + 运行时场景识别" 的双重判定策略
2.1判定流程图
js
┌─────────────────────────────────────────────────────────────────┐
│ App 启动 │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 第一道关卡:IDE 模拟器检测 │
│ uni.getSystemInfoSync().platform === 'devtools' │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────┴─────────────────┐
│ │
▼ 是 ▼ 否
┌───────────────────────────┐ ┌───────────────────────────────┐
│ 强制锁定开发环境 │ │ 进入真机判定(第二道关卡) │
│ develop │ │ dd.getRunScene │
└───────────────────────────┘ └───────────────────────────────┘
│ │
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────────┐
│ 连接测试库 │ │ 返回值判断 │
│ │ └───────────────────────────────┘
└───────────────────────────┘ │
│ ┌─────────────────┼─────────────────┐
│ │ │ │
│ ▼ release ▼ trial ▼ develop/其他/报错
│ ┌───────────────────┐ ┌───────────────┐ ┌───────────────────┐
│ │ 锁定正式环境 │ │ 锁定体验环境 │ │ 兜底回退 │
│ │ release │ │ trial │ │ 开发环境 develop │
│ └───────────────────┘ └───────────────┘ └───────────────────┘
│ │ │ │
│ ▼ ▼ ▼
│ ┌───────────────────┐ ┌───────────────┐ ┌───────────────────┐
│ │ 连接正式库 │ │ 连接测试库 │ │ 连接测试库 │
│ │ │ │ │ │ │
│ └───────────────────┘ └───────────────┘ └───────────────────┘
│ │ │ │
└─────────────────┴─────────────────┴─────────────────┘
│
▼
┌───────────────┐
│ 结束 │
└───────────────┘
2.2核心代码逻辑
- 定义环境配置:明确只有 release 才走 prodConfig,其他全走 testConfig;
- 辅助函数:统一处理版本更新并返回结果;
- IDE 拦截:只要是开发者工具,无论你选什么编译模式,platform 永远是 'devtools',这解决了"本地开发想默认连测试库,但 dd.getRunScene 有时乱返回 release"的问题;
- 真机场景识别:只有在真机上,才去问钉钉"我现在到底是什么版本";
- 兜底策略:如果 API 调用失败,为了安全起见,默认当做开发环境,宁可正式版连不上(连了测试库无数据),也不能让测试版污染正式库;
- 极端兜底;
js
// 1. 定义环境配置:明确只有 release 才走 prodConfig,其他全走 testConfig
const updateConfig = (version) => {
if (version === 'release') {
envConfig = { ...prodConfig } // 正式基地址
} else {
envConfig = { ...testConfig } // 测试基地址
}
}
const initEnvConfig = () => {
return new Promise((resolve) => {
// 2. 辅助函数:统一处理版本更新并返回结果
const resolveWithVersion = (version) => {
const finalVersion = normalizeEnvVersion(version)
updateConfig(finalVersion)
resolve({ envVersion: finalVersion, ...envConfig })
}
// 3. 【核心策略一】IDE 拦截
// 只要是开发者工具,无论你选什么编译模式,platform 永远是 'devtools'
// 这解决了"本地开发想默认连测试库,但 dd.getRunScene 有时乱返回 release"的问题
try {
const sys = uni.getSystemInfoSync()
if (sys.platform === 'devtools') {
resolveWithVersion('develop')
return // 直接结束,不再往下走
}
} catch(e) {}
// 4. 【核心策略二】真机场景识别
// 只有在真机上,才去问钉钉"我现在到底是什么版本"
if (typeof dd !== 'undefined' && dd.getRunScene) {
dd.getRunScene({
success: (res) => {
// res.envVersion 在正式版是 'release',体验版是 'trial'
resolveWithVersion(res.scene || res.envVersion)
},
fail: () => {
// 5. 【兜底策略】
// 如果 API 调用失败,为了安全起见,默认当做开发环境,
// 宁可正式版连不上(连了测试库无数据),也不能让测试版污染正式库
resolveWithVersion('develop')
}
})
return
}
// 6. 极端兜底
resolveWithVersion('develop')
})
}
三、 三大"血泪"踩坑记录
坑点一: process.env.NODE_ENV 是个骗子
- 现象 :在 HBuilderX 点击【运行】调试,然后直接在打开的支付宝工具里点【上传】。即使你选了"上传为正式版",代码里的 NODE_ENV 依然死死地等于 'development'
- 后果 :如果你代码里写了 if (NODE_ENV === 'development') return testConfig ,那你的正式版小程序将永远连接测试库。
- 避坑 : 在小程序代码逻辑中,彻底废弃 NODE_ENV 判断环境!
坑点二: uni.getAccountInfoSync 消失之谜
- 现象 :Uni-app文档说它是标准 API,但在钉钉小程序容器里直接报 TypeError: not a function
- 后果:页面白屏,JS 执行中断
- 避坑 :钉钉环境优先用 dd.getRunScene ,不要盲信 uni-app 的跨端 API 兼容性
坑点三:IDE 模拟器的"过度信任"
- 现象 :在编辑器里跑代码,dd.getRunScene 竟然返回 release(可能因为模拟器默认模拟线上)
- 后果:导致开发者在本地改代码时,不小心连上了正式库,删改了线上数据
- 避坑 :必须加 sys.platform === 'devtools' 这一道前置锁
四、总结
检验标准:
- 本地开发 :系统平台显示 devtools ,环境版本显示 develop ,BaseURL 是测试地址
- 体验版 :系统平台显示 android/ios ,运行场景显示 trial ,BaseURL 是测试地址
- 正式版 :系统平台显示 android/ios ,运行场景显示 release ,BaseURL 是正式地址
