多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

uni-app微信小程序多环境配置实战:从.env到发布检查的完整方案

uni-app微信小程序多环境配置实战:从.env到发布检查的完整方案 接手uniapp项目久了你会发现一个特别现实的问题——代码写完了联调的时候抠接口地址测试的时候抠接口地址上线前还在抠接口地址。一旦项目里对接了四五套后端环境手动切域名的方式就是给自己埋雷线上环境一不留神就把测试地址给带上去了。这类事故我见过不止一次有的发生在凌晨发版有的发生在给客户演示的当天下午。所以多环境配置这事不是锦上添花是一个项目从几个人协作走向正规化的必经之路。今天这篇就从实操角度把uni-app开发微信小程序时的多环境配置方案完整拆一遍。从最朴素的配置文件切换到基于Vite环境的自动化区分再到配合请求封装、自定义条件编译的完整落地流程每一步都会讲清楚为什么这么做以及实际项目中容易踩哪些坑。不管是刚接手uniapp的新手还是已经写了几个项目但环境管理全凭手改的老手这篇文章应该都能给你一些可以直接抄作业的东西。1. 多环境配置的整体设计与方案选型1.1 先搞清楚你的项目到底需要几套环境很多人在配环境的时候第一反应就是开发、测试、生产三件套。但实际项目里环境往往没这么简单。我经历过一个中型电商小程序光联调环境就有三套本地联调、开发服务器、集成测试再加上预发布和生产整整五套。配置环境之前先和你后端同学确认清楚这几个问题接口域名有几套如果后端全部是同一个网关地址那你只需要配一个BASE_URL。如果是微服务架构不同的业务域走不同的地址那你还要考虑用映射表管理一组域名而不是单纯一个字符串。有没有独立的静态资源地址有些项目的图片、文件上传走的是单独的OSS或CDN地址和业务接口域名不是同一个。这一项容易漏漏了之后你会发现在测试环境图片正常一上生产就裂图。WebSocket、H5跳转、支付回调这类特殊地址是不是也跟着环境走尤其是支付回调微信支付的后台回调地址是需要固定的但小程序体内部跳转的URL、公众号网页授权的地址多半是跟着环境变的。把这些信息列成一张表和你的目录结构放在一起就是你多环境配置的基础输入。别嫌这一步啰嗦我做过的项目里至少有三成环境配置返工都是因为刚开始没把环境清单梳理干净。1.2 几套主流配置方案各自的优缺点目前社区里常见的做法大致有三类。简单列一下后面实操都围绕它们展开。第一类单独一个config.js手动切环境。这是入门级做法。项目里建一个config目录放一个index.js里面写死几个环境对象然后用一行注释提醒自己上线前记得把isProd改成true。好处是直观坏处是必须靠人肉自觉。一旦团队成员多起来总有人忘记切甚至有人提个PR把你已经切好的环境又改回去因为这个吵架的情况也不少。第二类利用Vite的mode和.env文件自动区分。这是目前vue3版本的uniapp项目里最推荐的方案。uniapp内置的vite会读取项目根目录下的.env.development、.env.production你甚至可以自定义.env.test这样的文件。通过import.meta.env.MODE拿到的就是你当前启动或打包时用的环境标识脚本里可以基于它自动切换配置。它最大的优势是不用人肉记状态编译时是什么模式就自动用哪套环境团队成员之间不会互相干扰。第三类结合条件编译做平台维度的差异化配置。这个其实是对第二类的补充。uniapp支持#ifdef这种注释语法可以用来区分微信小程序、APP、H5等不同平台。比如同一个开发环境H5联调用的是本机局域网IP但微信小程序的开发工具里又需要用真机预览域名不能写localhost这种平台差异用条件编译处理是最顺的。我给你的建议也很直接——如果你还在用vue2的老项目第一类方案改造成本最低如果从vue3vite起步直接上第二类加第三类做兜底。我个人目前的主力方案就是用.env做环境切换条件编译处理平台差异config里保留一份常量映射表来收敛所有配置项。1.3 为什么我推荐在config层收敛而不是到处读env新手容易犯的一个错误是在api请求封装里直接读import.meta.env.VITE_API_BASE_URL在页面里又直接读import.meta.env.VITE_OSS_URL甚至图片处理、分享配置、地图SDK的key全部散落在各自文件里读env。这样每个文件都和环境变量耦合改起来烦躁排查的时候也容易漏。我更推荐的模式是在config目录里建一个环境映射文件把.env里读到的变量统一做一次转换组装成业务层需要的结构所有页面和请求封装只和这个配置文件打交道。举个例子你在.env里定义的可能是VITE_API_BASE_URLhttp://test-api.example.com但业务层关心的不是一个字符串而是一组行为——请求超时时长、是否需要模拟数据、上传文件的URL前缀、遇到401跳转到哪个登录页。这些组装逻辑放在config层业务代码就干净了后续加环境、加变量也只动config这一个文件。2. 环境配置的核心细节与落地要点2.1 manifest.json里的那些隐藏坑微信小程序的多环境配置绕不开manifest.json。开发者在配置文件里常常只盯着mp-weixin.appid这一个字段但实际有几个细节值得留意。appid跟着环境走的问题。很多公司一个小程序主体下有多个小程序账号比如开发版一个、正式版一个。你在manifest.json里只能写一个appid除非用HBuilderX的运行/发行弹窗去手动选择。这里就有一个很经典的痛点——你用测试小程序appid预览调了半天的微信登录都正常结果打包上线前把appid换成正式账号发现正式环境下用户登录需要重新配置合法域名、业务域名甚至支付商户号也可能对不上。所以一个比较稳妥的做法是把不同环境的appid和域名映射关系写进README发版走固定流程卡点检查。微信小程序的合法域名是死的。这一点是微信平台和普通web最大的差异。网页端联调随便localhost、跨域都没事但小程序开发环境下可以在开发工具里勾选不校验合法域名真机上这招是行不通的。所以到了真机预览或者体验版阶段你必须把环境对应的域名在微信公众平台配置成request合法域名。平时配置多环境的时候如果域名规划的不好上线前可能要改多处白名单很容易漏。建议在环境规划期就把API域名、下载域名、上传域名分开按固定的三级域名去设计比如api-dev.example.com、api-test.example.com、api.example.com这样小程序管理后台的白名单配置也能形成固定套路。2.2 环境变量的命名规范与管理环境变量能不能一劳永逸不踩坑命名规范是关键。Vite环境变量的规则是只有以VITE_开头的变量才会被import.meta.env暴露给前端代码其他变量不会被打包进去。这套机制本身很简单但实际项目里命名混乱带来的问题很常见。我见过的反面教材长这样.env.development里写API_URLxxx另一个同事在代码里写const baseUrl https://xxx直接硬编码。你问他的时候他说不知道有环境变量这回事。这种情况不是他能力不行是配置的可见性太差。所以命名和目录结构要做的显眼一点。建议在项目根目录固定以下几个文件.env # 公共变量所有环境共享 .env.development # 开发环境 .env.test # 测试环境 .env.production # 生产环境变量统一用VITE_前缀并且语义要直白VITE_ENV development VITE_API_BASE_URL http://dev-api.example.com VITE_OSS_URL http://dev-oss.example.com还有一个细节.env文件默认情况下Vite只会帮你加载特定后缀的文件比如开发模式加载.env.development生产构建加载.env.production。如果你要用自定义的.env.test得在打包脚本里通过--mode test告诉Vite去加载它这个我在第三部分实操里会详细说明。2.3 配置层缓存的坑小程序不是改完就生效的做多环境配置的时候很多人改完代码在小程序开发工具里一刷新发现请求还是走的老地址。原因多半不在你的代码而是小程序本身的缓存机制。微信小程序的代码包缓存和普通浏览器不一样你改了代码之后点击编译确实会重新打包但真机上已经打开过的小程序进程可能还挂着。遇到过最典型的情况是前端把环境切到了测试环境代码也重新上传体验版了但是手机端之前打开过正式环境的版本你重新扫码打开体验版页面栈或者业务数据里残留了旧的host看起来就像环境没生效。处理方式很简单微信小程序里加一个简单的启动日志在App.vue的onLaunch里打一下当前的接口前缀真机上看日志一目了然能省掉大量沟通成本。3. 实操过程一套可复制的完整环境配置流程3.1 初始化目录结构与env文件假设你已经有一个uniapp vue3项目第一步先把配置目录建好。我的习惯是这样的project-root/ ├── .env ├── .env.development ├── .env.test ├── .env.production ├── src/ │ ├── config/ │ │ ├── index.js │ │ └── env.js │ └── api/ │ └── request.js.env公共变量长这样VITE_ENV development VITE_APP_NAME 我的小程序.env.development开发环境VITE_ENV development VITE_API_BASE_URL http://192.168.1.100:8080 VITE_OSS_URL http://dev-oss.example.com.env.testVITE_ENV test VITE_API_BASE_URL https://test-api.example.com VITE_OSS_URL https://test-oss.example.com.env.productionVITE_ENV production VITE_API_BASE_URL https://api.example.com VITE_OSS_URL https://oss.example.com3.2 自定义mode的加载逻辑vite默认加载规则里没有.env.test这个概念。开发模式跑的是.env.development构建跑的是.env.production。所以test环境需要你给它定义一个mode。uniapp项目的构建脚本一般写在package.json或者HBuilderX的manifest里。用CLI创建的项目在package.json里这样加命令{ scripts: { dev:mp-weixin: uni -p mp-weixin, build:mp-weixin: uni build -p mp-weixin, build:test:mp-weixin: uni build -p mp-weixin --mode test, build:prod:mp-weixin: uni build -p mp-weixin --mode production } }注意这里的--mode test对应的就是Vite读取.env.test的逻辑。执行npm run build:test:mp-weixin的时候import.meta.env.MODE就是test同时.env和.env.test都会被加载进来文件的优先级是.env.mode覆盖.env。如果你不是在CLI项目里而是用HBuilderX可视化界面点点点的那也没问题——HBuilderX的 manifest.json 里源码视图下可以在mp-weixin节点下配置env相关字段或者干脆就建立三个不同名字的manifest来区分环境但那个方式维护成本略高我还是更建议用手动脚本。3.3 写配置文件层避免到处读envconfig/env.js 作用是把原始环境变量转为业务对象// src/config/env.js const env import.meta.env export function getEnvConfig() { return { env: env.VITE_ENV || development, apiBaseUrl: env.VITE_API_BASE_URL || , ossUrl: env.VITE_OSS_URL || , appName: env.VITE_APP_NAME || 默认名称 } } export const isDev env.VITE_ENV development export const isTest env.VITE_ENV test export const isProd env.VITE_ENV productionconfig/index.js 一次性组装业务参数// src/config/index.js import { getEnvConfig, isDev, isProd } from ./env const conf getEnvConfig() export const config { env: conf.env, // 统一接口前缀 baseUrl: conf.apiBaseUrl, // 上传文件用的地址和接口地址可能不一样 uploadUrl: conf.ossUrl /upload, // 静态资源访问地址 staticUrl: conf.ossUrl, // 开发模式下可以开调试 debug: isDev, // 线上环境统一走https、不开vconsole之类的开关 enableVconsole: !isProd } export default config这样一来业务层所有的调用都从config里取比如请求封装、页面跳转、文件夹上传全部只依赖config。后面要加环境就改env文件加一个对应的变量改config组装逻辑其他代码动都不用动。3.4 请求封装的统一处理光配好地址还不行请求封装里对这个地址的使用方式也决定了整个配置会不会生效。我在request.js里一般是这么处理baseURL的// src/api/request.js import config from /config/index.js export function request(options) { const { url, method GET, data {}, header {} } options return new Promise((resolve, reject) { uni.request({ url: config.baseUrl url, method, data, header: { Content-Type: application/json, ...header }, success: (res) { // 统一的业务码处理、401跳登录之类的逻辑 if (res.data.code 0) { resolve(res.data.data) } else { uni.showToast({ title: res.data.message || 请求失败, icon: none }) reject(res) } }, fail: (err) { // 真正的失败原因网络问题、域名白名单问题都在这里 console.error([request error] ${config.baseUrl}${url}, err) reject(err) } }) }) }这个封装里有个容易被忽略的点对config.baseUrl保留了一个显式的引用记录。你在fail的回调里打印出来的完整URL能够一眼看出当前请求打到了哪个环境。我们团队后来排查环境问题的效率提高了不少靠的就是这条日志。这里也给新手提个醒报错信息一定要带环境信息不然线上问题排查会变成猜谜游戏。3.5 条件编译处理平台差异有些配置项是平台维度的和环境维度正交。举个例子开发环境下H5端联调地址写http://localhost:8080没问题但微信小程序端跑真机预览localhost指向的是手机自己必须用局域网IP或线上测试域名。这种情况下用环境变量去区分是不够的需要条件编译来兜底。条件编译是uniapp里一个很有意思的机制它靠注释语法控制编译结果// src/config/env.js const platformConfig { // #ifdef MP-WEIXIN mpAppId: wx123456 // #endif // #ifdef H5 h5AppId: // #endif }还有一个非常常见的应用场景分享配置。微信小程序里onShareAppMessage的路径参数不同环境的页面路径往往不一样比如测试环境下为了便于定位你需要把某个列表页的query带上fromtest。这种逻辑你写在环境变量里会越来越乱用条件编译包一下反而清爽。不过要提醒一句条件编译虽然好用但不能滥用。项目里的#ifdef如果到处都是代码的可读性会急剧下降后期维护的人根本分不清哪些分支是清理过的、哪些已经失效了。我的经验是条件编译只用来处理平台差异不做环境差异环境的差异一律走.env。3.6 上传微信小程序前的环境确认清单环境配置做完之后真正的考验是发布流程。尤其是团队里有多个前端、多个后端谁改了什么没人说得清。我在这个环节吃过亏之后给自己定了一个固定的checklist分享出来可以直接用第一编译产物检查。微信小程序打包产物一般在dist/build/mp-weixin目录下。上传前全局搜索这个文件夹里有没有遗留的localhost、内网IP甚至更隐蔽的192.168字样的地址。正规团队会在CI上加一道检查命令命令行一行就能搞定grep -r localhost\|192.168. dist/build/mp-weixin --include*.js | wc -l如果这个数字大于0就别传了先回去查_env相关文件的加载逻辑。第二微信公众平台合法域名检查。在小程序后台的开发管理-开发设置-服务器域名里确认request合法域名里包含了当前环境要用的API域名。测试环境因为只有体验版在用可能没加白名单真机预览时会出现不在以下合法域名列表中的报错这个报错几乎100%可以定位到域名白名单问题。第三appid检查。用HBuilderX云打包或者本地CLI跑出来的包在project.config.json里确认编译出的appid是不是你预期环境对应的账号。这个检查不能省我遇到过开发工具的appid和CI上面的不一致导致微信登录死活调不通排查了一个下午才意识到是两个小程序账号。第四环境日志开关。理论上生产环境不应该打开调试日志、vconsole、红点提示。这些开关建议从config层控制而不是手动注释。说白了你在.env.production里把VITE_ENVproduction一设置isProd就会自动把vconsole禁用掉不然哪天忘了删调试代码线上用户就可能看到一堆奇怪的日志输出。4. 常见问题与排查技巧实录4.1 开发模式个人踩过最多次的坑env文件变更不生效很多刚用Vite环境变量的同学遇到的情况是改了.env.development里的地址保存刷新发现还是旧地址。原因在于Vite的环境变量是在启动时加载的部分配置还会被缓存到node_modules/.vite或者小程序开发工具的缓存里。改完env文件一定要重启dev服务HBuilderX里就是重新运行到微信小程序CLI项目就是CtrlC然后重新npm run dev。有时候小程序开发工具已经有编译缓存也要在那个地方点一下清缓存并重新编译。这不是玄学是工具链的机制问题不是你的代码逻辑问题。4.2 配置文件里出现undefined或者空字符串用import.meta.env.VITE_XXX拿到undefined的情况90%是因为变量名拼写和env文件里的不对应。这里有个小坑Vite的环境变量赋值必须严格写成VITE_API_BASE_URLxxx中间有没有引号都能读取但是一旦你多写了空格VITE_API_BASE_URL xxx等号两边留白Vite解析的时候可能直接把变量名带着空格传给代码导致匹配不到。另外如果你配置变量值里面有特殊字符比如、#要记得用引号包起来。后端联调地址带token参数的话这一条极易中招。4.3 微信小程序开发工具里显示请求正常真机就失败这个问题排查顺序一般很固定先从域名白名单看起。开发工具里可以勾选不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书但这一项只对开发工具有效真机完全不认。第二个要看TLS版本老手机的系统TLS版本过低如果后端服务器只支持TLS1.3同样会有兼容问题。第三个排查项是IPv6有些新环境纯IPv6部分安卓机器解析有问题。这三个按顺序排一遍基本能定位90%的真机网络异常。4.4 多环境导致的环境串访问题场景测试环境App跳转到了生产环境的页面或者反过来。这通常是分享链接、扫码跳转、或者某个web-view里带的固定H5地址导致的。对策所有跨端跳转的URL、path统一从config里取不要在业务代码里写死。config里同一个变量在不同环境的值不一样业务逻辑自然跟着走。如果历史代码里已经写死了一堆地址建议全局搜索https://相关字段一个一个审查归属。4.5 遇到微信小程序报url not in domain list的一种冷门情况这个报错一般就是白名单问题但有一种冷门情况是后端反代完返回了302跳转跳转后的地址不在白名单里。微信小程序的request只认最终响应的地址中间跳转的地址不符合域名校验也会报错。这时候前端没法处理要把后端拉过来一起排查。如果后端说我们没动过让他去网关层看看重定向逻辑多半是网关环境分开配了测试环境用的跳转域名和生产不一致。最后再分享一个实用经验多环境配置做到了一定阶段其实你会意识到难点不在技术而在人和流程之间怎么对齐。技术方案再丝滑团队里总有人习惯直接复制别人的config文件或者上线前临时改地址这都会让环境管理回到起点。我在项目里后来强制做了一件事把环境标识打在打包产物的可见位置。具体做法是在小程序启动页加一个环境角标开发环境显示DEV测试环境显示TEST生产环境不显示。这个角标用config里的env字段控制成本极低但效果奇好——测试同事拿到的安装包是什么环境一眼就知道产品验收的时候也不会再问你这是新版还是旧版。这种小细节比在文档里写十遍上线前请检查环境都管用。多环境配置没有银弹不同项目、不同团队规模适合的方案都会不一样。但你只要把env文件的加载机制吃透把config层收敛好把发布检查清单固化到流程里这个事基本就稳了。后续如果项目继续扩大可以考虑再往CI方向走把环境检查脚本自动跑起来那又是另外一个层次的事情了。
返回列表