ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

uni-app多环境配置详解:HBuilderX与CLI项目的发行方案

uni-app多环境配置详解:HBuilderX与CLI项目的发行方案 做uni-app项目最烦的不是写业务而是每次发行前改配置。API地址、小程序AppID、调试开关、统计平台的key这些东西在开发、测试、生产三个环境里完全不一样。手动改吧改完忘了改回来测试包连了正式接口上线包连了测试库这种事故我见过太多了。这篇文章会从HBuilderX的发行机制说起把多环境配置这件事彻底讲透。无论你是用HBuilderX直接创建的项目还是基于vue-cli的CLI项目都有对应的落地方案。1. 多环境发行到底在解决什么问题1.1 一个真实场景每次发行前你都在改什么先复盘一下一个典型的小程序或App项目从开发到上线要经历哪几个环境开发环境接口一般指向本地局域网或mock服务域名是http://192.168.x.x:8080这种调试开关全开方便看日志。测试环境接口指向测试服务器数据是脏数据域名是https://test-api.xxx.com给测试同学验收用。生产环境接口指向正式服务器域名是https://api.xxx.com所有调试输出全部关闭。除了接口地址还有很多东西跟着环境变微信小程序的AppID开发版、体验版、正式版可能是不同的账号主体、分享链接、支付回调、埋点上报地址、图片上传的CDN路径。甚至原生App的包名和应用名称在测试环境和生产环境也应该是两套不然测试包和正式包会互相覆盖安装。这些配置如果散落在各个页面的代码里每次发行都要全局搜索一遍很容易漏。更麻烦的是如果团队里有两个人同时改Git提交记录会乱成一团。1.2 错误做法带来的代价我的经验是多环境配置做得不好的项目迟早会出事故。举几个常见的坑发行前手动改URL改完上线后代码没同步回主干下个迭代所有人都在错误配置上开发。一个config文件复制三份dev.js、test.js、prod.js改接口字段时只改了其中一个测试环境跑通了生产环境爆了。条件编译写得满天飞// #ifdef H5 let baseUrl https://h5-api.xxx.com // #endif // #ifdef MP-WEIXIN let baseUrl https://wx-api.xxx.com // #endif这种写法只能区分平台区分不了环境而且代码一多维护成本直接失控。真正的问题是很多开发者没有意识到HBuilderX在运行和发行时本来就提供了环境变量机制只是没有系统地用好它。下面先把HBuilderX的发行机制讲清楚再给具体方案。2. HBuilderX发行机制里必须搞懂的三个点2.1 “运行”和“发行”背后是两套编译模式HBuilderX的菜单栏里“运行”和“发行”是两个完全不同的动作它们的编译模式也不一样运行比如运行到浏览器、运行到微信开发者工具走的是development模式process.env.NODE_ENV的值是development编译速度更快带热更新和sourcemap。发行比如发行到微信小程序、原生App云打包走的是production模式process.env.NODE_ENV的值是production做压缩、混淆产物更小。这一点非常重要因为它是多环境方案的基础。也就是说开发和正式发布之间天然就有一个环境开关可以用不需要你自己去判断当前到底是在开发还是上线。2.2 process.env 在 uni-app 中到底怎么用很多人在uni-app里不敢用process.env觉得这是Node.js的东西其实在编译阶段webpack/vite已经把所有process.env.xxx的引用替换成了对应的值。也就是说你在代码里写的process.env.NODE_ENV在编译完成后会被直接换成production或development这样的字符串。除了NODE_ENVuni-app编译时还会注入一些固定变量UNI_PLATFORM当前编译的平台比如h5、mp-weixin、app-plus。UNI_APP_IDmanifest.json里配置的appid。VUE_APP_*你自己定义的环境变量必须以VUE_APP_开头否则不会暴露给前端代码。举个例子// 在CLI项目中.env文件里写 VUE_APP_BASE_URL https://test-api.xxx.com // 业务代码里直接读 console.log(process.env.VUE_APP_BASE_URL)编译后就会变成console.log(https://test-api.xxx.com)2.3 默认模板和 CLI 模板方案完全不同这是很多教程没讲清楚的地方。同样是HBuilderX创建的项目分两种类型HBuilderX默认模板项目根目录可能只有pages、static、manifest.json、main.js没有src目录依赖HBuilderX内置编译器。这种项目想跑npm run build是跑不起来的因为没安装uni-app的CLI依赖。CLI模板通过vue create -p dcloudio/uni-preset-vue创建有完整src目录有package.json和node_modules可以用npm命令自由控制构建流程。这两个类型对应的多环境方案不一样项目类型推荐方案优点HBuilderX默认模板配置文件 环境开关变量零依赖开箱即用CLI模板.env--mode环境切换精确适合自动化下面分别给方案你根据自己的项目类型直接抄就行。3. 方案一纯 HBuilderX 项目的配置驱动发行这一节是写给默认模板项目的不用装任何东西靠一个全局配置中心解决问题。3.1 先搭一套“环境配置中心”在项目根目录下新建一个config文件夹里面放两个文件env.js和index.js。env.js负责定义当前环境开关// config/env.js // 当前环境dev / test / prod // 手动切换时只需要改这一个变量 const BUILD_ENV prod export default BUILD_ENVindex.js负责返回环境对应的配置对象// config/index.js import BUILD_ENV from ./env const CONFIG { dev: { BASE_URL: http://192.168.1.100:8080, UPLOAD_URL: http://192.168.1.100:8080/upload, DEBUG: true, APP_ID: wxdev123 }, test: { BASE_URL: https://test-api.xxx.com, UPLOAD_URL: https://test-api.xxx.com/upload, DEBUG: true, APP_ID: wxtest456 }, prod: { BASE_URL: https://api.xxx.com, UPLOAD_URL: https://api.xxx.com/upload, DEBUG: false, APP_ID: wxprod789 } } // 借助运行/发行的NODE_ENV差异开发环境自动走dev配置 let envName BUILD_ENV if (process.env.NODE_ENV development) { envName dev } const config CONFIG[envName] || CONFIG.prod export default config这里有几个设计细节值得说明一下我在index.js里做了process.env.NODE_ENV判断这样开发时用“运行”功能自动走dev配置不需要改任何东西。BUILD_ENV开关保留的意义是发行时如果你要出测试包才需要手动改成test正常出正式包保持prod就行。APP_ID是业务层需要的参数跟manifest.json里的appid不是一回事具体看你的业务需求。3.2 把配置挂到全局业务代码统一取用配置中心建好后需要在入口文件里挂载到全局不然每个页面都去import很麻烦。在main.js里加两行// main.js import Vue from vue import App from ./App import config from ./config Vue.prototype.$config config // 页面里用 this.$config uni.$config config // 非页面代码里用 uni.$config然后封装一个统一的request.js所有接口请求都走这里// utils/request.js const BASE_URL uni.$config.BASE_URL function request(url, data {}, method GET) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL url, data, method, success: (res) { if (uni.$config.DEBUG) { console.log([request] ${method} ${url}, res.data) } resolve(res.data) }, fail: reject }) }) } export default request这样业务页面里完全不需要关心当前是哪个环境只需要// pages/index/index.vue import request from /utils/request export default { methods: { async getList() { const res await request(/api/list, { page: 1 }) this.list res.data } } }这个方案落地之后日常开发你只需要用“运行”按钮它会自动走dev配置正式上线直接用“发行”按钮自动走prod配置只有出测试包时需要动一下env.js里的开关。3.3 发行时如何切换环境而不改代码很多团队卡在这一步测试包要经常出每次发行都去改env.js就算只有一个变量也容易忘改回来。我用的办法是写一个简单的Node脚本一键切换开关。项目根目录新建build/release.js// build/release.js const fs require(fs) const path require(path) const target process.argv[2] || dev if (![dev, test, prod].includes(target)) { console.error([release] 参数必须是 dev / test / prod) process.exit(1) } const file path.resolve(__dirname, ../config/env.js) const content // 由 release.js 自动生成请勿手动修改\nconst BUILD_ENV ${target}\n\nexport default BUILD_ENV\n fs.writeFileSync(file, content, utf8) console.log([release] BUILD_ENV 已切换为: ${target})在package.json里配上命令没有package.json就新建一个{ scripts: { env:dev: node build/release.js dev, env:test: node build/release.js test, env:prod: node build/release.js prod } }之后出包流程就变成命令行执行npm run env:test。打开HBuilderX点“发行 - 小程序-微信”。完事。HBuilderX的发行面板里如果支持配置外部命令可以把这几个脚本添加到外部命令列表点一下按钮就完成切换不用切到终端。如果你的团队用的是默认模板这个方案基本够用了。4. 方案二CLI 项目用 .env 精确控制多环境如果你的项目本来就是CLI模板前面那套也能用但会有更优雅的方案配合Vue CLI的mode机制用.env文件管理环境变量。这种方案的好处是环境切换完全由命令驱动不需要手动改任何代码。4.1 先确认你的项目是不是 CLI 项目看两个标志根目录下有没有src文件夹pages、static都在src里面。根目录下有没有vue.config.js和完整的package.json且dependencies里有dcloudio/uni-app相关包。如果都符合那恭喜你可以用这一节的方案。如果你不确定可以去命令行里试试npx uni --version能正常输出版本号就是CLI项目。如果是CLI项目用HBuilderX打开时也能正常识别运行和发行都走CLI构建流程。4.2 .env 文件到底怎么写Vue CLI的mode机制是这样的执行uni build -p mp-weixin --mode test时会自动加载根目录下的.env.test文件把文件里的变量注入process.env。项目根目录下创建三个文件.env.development开发环境NODE_ENVdevelopment VUE_APP_BASE_URLhttp://192.168.1.100:8080 VUE_APP_DEBUGtrue.env.test测试环境NODE_ENVproduction VUE_APP_BASE_URLhttps://test-api.xxx.com VUE_APP_DEBUGtrue.env.production生产环境NODE_ENVproduction VUE_APP_BASE_URLhttps://api.xxx.com VUE_APP_DEBUGfalse有几点需要特别注意VUE_APP_前缀不能少uni-app在CLI工程里基于Vue CLI只有以VUE_APP_开头的变量才会被打包进前端代码其他变量只能在vue.config.js里用。NODE_ENV一般不用写在.env里因为uni build本身就表示生产构建uni serve表示开发构建。但如果某个模式下你特别需要覆盖它也可以像上面那样显式写。每个.env文件之外还可以有.env.local作为本机私有的覆盖文件一般用来覆盖本机调试地址不需要提交到Git。4.3 配置 package.json 的构建命令在package.json的scripts里加上面向不同环境的命令{ scripts: { dev:h5: uni -p h5, dev:mp-weixin: uni -p mp-weixin, build:test:h5: uni build -p h5 --mode test, build:test:mp-weixin: uni build -p mp-weixin --mode test, build:prod:h5: uni build -p h5 --mode production, build:prod:mp-weixin: uni build -p mp-weixin --mode production } }注意看--mode test对应加载.env.test--mode production对应加载.env.production。如果你要出一个测试环境的微信小程序包只需要执行npm run build:test:mp-weixin构建产物在dist/build/mp-weixin用微信开发者工具导入这个目录就行。比起在HBuilderX里手动切换命令行的方式更适合接CI/CD流水线本地开发、测试包、正式包各跑各的互不干扰。4.4 发行微信小程序全流程超详细结合热搜里“hbuilderx 发行 微信小程序 超详细步骤”的需求我把整个流程串一遍。这里分两种情况但前置工作一样。第一步注册小程序账号并拿到AppID到微信公众平台注册小程序类型选“企业”或“个人”注册完后在“开发管理 - 开发设置”里能看到AppID。测试号一般不需要单独AppID但正式项目每个环境最好用不同的AppID这样开发版、体验版、正式版可以同时存在。第二步在manifest.json里配置AppID打开manifest.json找到“微信小程序配置”节点{ mp-weixin: { appid: wxprod789, setting: { urlCheck: false }, usingComponents: true } }如果你是CLI项目且想按环境动态取AppID可以在vue.config.js里读取环境变量再覆盖// vue.config.js module.exports { transpileDependencies: [uni-app], configureWebpack: { plugins: [] }, // uni-app的编译配置 chainWebpack: (config) { config.plugin(define).tap((args) { args[0][process.env].UNI_APP_MP_WEIXIN_APPID JSON.stringify(process.env.VUE_APP_WX_APPID) return args }) } }不过说实话manifest.json里的AppID能不能被环境变量完全接管不同版本uni-app表现有差异我最稳的做法是模板项目直接改manifest.jsonCLI项目用.env配合脚本在发行前覆盖。第三步执行构建HBuilderX默认模板项目点“发行 - 小程序-微信”在弹出的对话框里填小程序名称点“发行”完事。CLI项目执行npm run build:test:mp-weixin或npm run build:prod:mp-weixin。第四步打开微信开发者工具导入产物CLI项目的产物在dist/build/mp-weixinHBuilderX默认模板的产物也在dist/build/mp-weixin有的版本在unpackage/dist/build/mp-weixin。打开微信开发者工具 - 导入项目 - 选择这个目录 - 填上AppID。第五步处理合法域名问题开发调试阶段可以在微信开发者工具的“详情 - 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”这样测试环境即使域名没备案、不是https也能调试。但上线前一定要到微信公众平台的“开发管理 - 开发设置 - 服务器域名”里把生产环境的API域名加到request合法域名里。这一步漏掉的后果是体验版、线上版所有接口全部请求失败而且控制台只给一行“url not in domain list”排查起来特别费劲。第六步上传版本并提交审核微信开发者工具点“上传”填版本号和备注到微信公众平台的“版本管理”里把上传的版本设为体验版或提交审核。版本号记得跟你在代码里改的版本保持一致不然会乱。5. 方案三脚本自动切换环境适合需要一键发行如果你觉得手动执行命令还是太烦或者团队里有非技术同事也需要出包那可以更进一步把所有流程脚本化。5.1 用 Node 脚本自动改写环境开关这一节我给一个通用思路。不论你是默认模板还是CLI项目都可以写一个脚本一键完成三件事切换环境、构建、复制产物。以CLI项目为例新建build/release.js// build/release.js const { execSync } require(child_process) // 读取命令行参数 const target process.argv[2] || prod const platform process.argv[3] || mp-weixin const modeMap { dev: development, test: test, prod: production } const mode modeMap[target] console.log([release] target${target} platform${platform} mode${mode}) // 执行uni build命令 const cmd uni build -p ${platform} --mode ${mode} execSync(cmd, { stdio: inherit }) console.log([release] 构建完成产物在 dist/build/${platform})在package.json里配上{ scripts: { release:test: node build/release.js test mp-weixin, release:prod: node build/release.js prod mp-weixin } }以后出测试包一行命令npm run release:test完事。这个脚本还可以继续加功能构建完自动压缩产物、自动上传到自己的服务器、自动调用微信开发者工具的CLI上传代码这些都是后面可以延伸的方向。5.2 把脚本配成 HBuilderX 外部命令HBuilderX支持配置外部命令你可以在“工具 - 外部命令”里把上面这些命令加进去比如名称发行测试包命令npm run release:test之后在HBuilderX里点一下菜单就能执行不用切出IDE也不用手动敲命令。这个功能对非技术的同事特别友好你只需要告诉他们出测试包点这个出正式包点那个。5.3 顺带解决的版本号管理问题脚本化的另一个好处是可以顺带管理版本号。小程序和App的版本号是需要递增的但手动改很容易忘。可以在release.js里加上自动读取和递增逻辑const fs require(fs) const path require(path) // 读取manifest.json自动递增版本号 const manifestPath path.resolve(__dirname, ../src/manifest.json) const manifest JSON.parse(fs.readFileSync(manifestPath, utf8)) const [major, minor, patch] manifest.versionName.split(.).map(Number) const newPatch patch 1 manifest.versionName ${major}.${minor}.${newPatch} manifest.versionCode manifest.versionCode ? manifest.versionCode 1 : 1 fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), utf8) console.log([release] 版本号已更新为 ${manifest.versionName} (${manifest.versionCode}))另外有一点要提醒原生App不同环境的包名一定要区分。比如Android的包名测试环境用com.xxx.app.test生产环境用com.xxx.app。否则测试包和正式包装在同一个手机上会互相覆盖安装数据也串了。这个是在manifest.json - App模块配置 - Android包名里配置的。6. 常见问题与排查技巧实录6.1 环境变量改了不生效这个问题出现频率最高。排查看三个地方HBuilderX默认模板改了config/env.js之后必须重新点一次“运行”或“发行”不能只刷新页面因为process.env的注入发生在编译阶段不是运行时。CLI项目改了.env文件后必须重启npm run dev或npm run build进程webpack缓存里可能还留着旧值。变量名是否带VUE_APP_前缀CLI项目里如果变量名没有VUE_APP_前缀代码里访问process.env.xxx拿到的是undefined而且不会报错排查起来非常隐蔽。这个题我建议在代码里做一层保护const BASE_URL process.env.VUE_APP_BASE_URL || https://api.xxx.com这样即使环境变量没配也有一个兜底地址不会直接白屏。6.2 微信开发者工具打不开或白屏这是HBuilderX开发微信小程序的经典问题。分几种情况打不开先确认微信开发者工具有没有开启“服务端口”。在微信开发者工具里点“设置 - 安全设置”打开“服务端口”然后重启HBuilderX再试。打开了但白屏先看控制台有没有报错常见原因是AppID没填或者填错了也可能因为项目路径含中文导致编译失败。H5能跑小程序不行有些API在小程序端不受支持比如window对象、document对象。排查时把页面逐步注释掉二分定位。还有一个很容易忽略的问题HBuilderX运行时如果改了端口小程序里可能连不上。HBuilderX运行到浏览器默认端口是8080如果被占用了可以在manifest.json里配置{ h5: { devServer: { port: 8081 } } }改完重新运行刷新页面就行。6.3 不同环境 AppID 和合法域名问题多环境方案落地后最容易踩的坑是代码里环境切换对了但微信小程序的AppID忘了切换。比如你出了一个测试环境的包结果manifest.json里填的是正式环境的AppID。微信开发者工具导入时直接报错说AppID和项目不匹配。这种情况我建议你在config/index.js里加一个环境标识并在小程序启动时打到控制台// main.js console.log([env] 当前环境${envName}) console.log([env] BASE_URL${config.BASE_URL})出包后第一件事就是打开控制台看这个日志确认是不是自己想要的环境。合法域名的问题前面提过再强调一次开发调试可以勾选“不校验合法域名”但这只是临时的。上线前必须到微信公众平台配置而且要区分request、uploadFile、downloadFile三类域名不要全部填到request里。6.4 配置出错后的快速排查清单我把平时排查多环境问题的顺序整理成了一张表按顺序检查基本能解决90%的问题检查项操作方法常见结果NODE_ENV是否正确在代码里console.log(process.env.NODE_ENV)development / production环境开关变量检查env.js里的BUILD_ENV或CLI的--mode参数dev / test / prod配置文件字段打印uni.$config核对BASE_URL、APP_ID等字段是否跟预期一致编译缓存停掉编译进程删除dist和unpackage目录重新编译干净构建微信开发者工具缓存删除导入的项目重新导入重新编译合法域名微信公众平台 - 开发管理 - 服务器域名域名是否在列表中另外如果你修改了manifest.json里的AppIDHBuilderX有时不会自动重新读取需要在HBuilderX里右键项目 -“重新识别项目类型”或者重启HBuilderX。这个细节折磨过我很久网上翻半天也找不到答案实际就是IDE缓存的问题。我个人在实际操作中的体会是多环境配置这件事花半小时做一次省下来的时间是按天算的。哪怕是最简单的“配置文件环境开关”方案也比每次发行前手动搜索替换URL强一百倍。如果你是个人开发者或者小团队方案一足够用了如果是接持续集成的项目方案二和方案三才是正道。最后再分享一个小技巧不管用哪种方案都把环境信息在启动日志里打出来一方面自己心里有数另一方面同事拿到包也能一眼看出是哪个环境的能避免很多不必要的沟通成本。
返回列表