前言本文基于真实的线上项目介绍如何用一套代码支持多个地区、多种平台微信/支付宝小程序、H5 等、多种部署环境开发/测试/生产的构建与打包并给出具体代码与配置说明。开始之前大家先想象这样一个场景同一套业务要同时给 A 省、B 省、C 省上线微信小程序和支付宝小程序每个省的标题、首页内容、支付方式、后端网关地址都不一样还要区分 dev / qa / prod 多套环境。如果每一套都复制一份代码维护成本会很快失控。那么我这篇文章讲的就是怎样通过「一套代码 一组脚本」把这些组合全部收拢起来统一维护。一、需求与目标维度说明示例地区DISTRICT不同省份/地区不同应用标题、页面配置、manifest新疆、安徽、江苏、吉林…平台UNI_PLATFORM不同运行端微信小程序 mp-weixin、支付宝 mp-alipay、H5环境DEPLOY不同 API 与部署目标dev、qa02、release_anhui、xinjiang_prod…目标通过一条 npm 脚本即可确定「地区 平台 环境」打出对应产物无需改代码。二、整体思路npm script (cross-env 注入环境变量) ↓ process.env.UNI_PLATFORM / DISTRICT / DEPLOY / NODE_ENV ↓ vue.config.js 加载时先执行 preBuild.js类似于webpack自定义plugin的效果 ↓ preBuild.js 根据上述变量生成 CLIENT_TYPE、APP_TITLE、API_BASE_URL 等 ↓ 动态生成 pages.json、manifest.json按地区合并 ↓ Webpack 通过 EnvironmentPlugin 将变量注入业务代码 ↓ 构建产物中 process.env.XXX 被替换为常量核心有以下三点环境变量驱动用UNI_PLATFORM、DISTRICT、DEPLOY等控制整条构建链。构建前预处理在 Webpack 之前跑preBuild.js统一把「平台/地区/环境」转成业务需要的CLIENT_TYPE、API_BASE_URL、APP_TITLE等。配置与代码注入用 Webpack 官方EnvironmentPlugin把process.env中的键注入到前端代码保证运行时代码能拿到当前构建的「地区/平台/环境」。三、具体实现步骤与代码说明3.1 用 cross-env 在 npm script 中传参不同操作系统下设置环境变量方式不同使用 cross-env 可统一写法。安装npm i -D cross-envpackage.json 中的脚本示例{ scripts: { build:mp-weixin:anhui:release_anhui: cross-env NODE_ENVproduction UNI_PLATFORMmp-weixin DISTRICTanhui DEPLOYrelease_anhui vue-cli-service uni-build, dev:mp-weixin:anhui:release_anhui: cross-env NODE_ENVdevelopment UNI_PLATFORMmp-weixin DISTRICTanhui DEPLOYdev_anhui vue-cli-service uni-build --watch } }含义简述cross-env KEYvalue会在当前 Node 进程中设置process.env.KEY value。后面执行的vue-cli-service uni-build与它加载的vue.config.js、preBuild.js都运行在同一进程因此能直接读到这些环境变量。命名约定dev/build平台地区环境便于一眼看出打的是哪一套。关于--watch开发脚本如dev:mp-weixin:...末尾加上--watch表示开启监听模式当代码或配置文件变更时会自动重新编译对应平台/地区/环境的产物无需每次手动重新执行命令适合本地联调和反复修改。常见变量变量含义示例NODE_ENV开发/生产development / productionUNI_PLATFORM平台mp-weixin、mp-alipay、h5DISTRICT地区anhui、xinjiang、jiangsuDEPLOY部署环境dev、qa、release_anhuiCUSTOM_TAB可选自定义 tab 配置如 pay、order_search3.2 统一配置preBuild.config.js把「平台 → 客户端类型」「地区 → 应用标题」「部署环境 → API 地址」等做成映射表便于维护和扩展。config/preBuild.config.js// 平台与客户端类型用于接口等 const CLIENT_TYPE_MAP { h5: 1, mp-weixin:2, mp-alipay:3, mp-baidu: 4, mp-toutiao: 5, mp-qq: 6 } // 地区与应用标题等展示配置 const DISTRICT_CONFIG_MAP { jilin: { APP_TITLE: xxx }, xinjiang: { APP_TITLE: xxx }, // ... 其他地区 } // 部署环境与 API 根地址示例域名为占位 const API_BASE_URL_MAP { dev: https://api-dev.example.com/mobile-app/api, qa: https://api-qa02.example.com/mobile-app/api, release_regionA: https://api-release-regionA.example.com/mobile-app/api, // ... 其他环境 } module.exports { CLIENT_TYPE_MAP, DISTRICT_CONFIG_MAP, API_BASE_URL_MAP }后续新增地区或环境时只需在此处增补配置无需改构建脚本逻辑。3.3 构建前预处理preBuild.js在 Webpack 读取vue.config.js之前需要把「平台/地区/环境」转成业务和配置生成器使用的变量。因此把preBuild.js放在vue.config.js最前面执行。build/preBuild.js/** * 设置自定义的 process.env.X 需同时在 vue.config.js 的 * configureWebpack.plugins 里加入 EnvironmentPlugin 对应 key */ const { CLIENT_TYPE_MAP, DISTRICT_CONFIG_MAP, API_BASE_URL_MAP } require(../config/preBuild.config) // 版本号等固定值示例 process.env.VERSION 1.0.0 // 由「平台」得到客户端类型 process.env.CLIENT_TYPE CLIENT_TYPE_MAP[process.env.UNI_PLATFORM] // 由「地区」得到应用标题缺省用 jilin process.env.APP_TITLE DISTRICT_CONFIG_MAP[process.env.DISTRICT || xuzhou].APP_TITLE // 由「部署环境」得到 API 根地址缺省用 dev process.env.API_BASE_URL API_BASE_URL_MAP[process.env.DEPLOY || dev] // 可选自定义 tab 等未传则空字符串 process.env.CUSTOM_TAB process.env.CUSTOM_TAB || // 便于排查构建时打印当前维度 console.log(------------------------------) console.log(NODE_ENV:, process.env.NODE_ENV) console.log(DEPLOY:, process.env.DEPLOY) console.log(UNI_PLATFORM:, process.env.UNI_PLATFORM) console.log(DISTRICT:, process.env.DISTRICT) console.log(CLIENT_TYPE:, process.env.CLIENT_TYPE) console.log(API_BASE_URL:, process.env.API_BASE_URL) console.log(------------------------------) // 根据 DISTRICT 等动态生成 pages.json、manifest.json require(./pages.json.js) require(./manifest.json.js)要点只读不写从process.env读取由 cross-env 注入的UNI_PLATFORM、DISTRICT、DEPLOY。派生变量写入CLIENT_TYPE、APP_TITLE、API_BASE_URL、CUSTOM_TAB等供后续 Webpack 和动态配置使用。执行顺序本文件在vue.config.js的顶部被require因此先于 Webpack 配置执行下面的pages.json.js、manifest.json.js会用到当前的process.env.DISTRICT等。3.4 在 vue.config.js 中接入 preBuild 与 EnvironmentPluginvue.config.jsconst webpack require(webpack) const path require(path) // 必须最先执行注入 CLIENT_TYPE、API_BASE_URL 等并生成 pages.json / manifest.json require(./build/preBuild.js) module.exports { configureWebpack: { resolve: { alias: { // 按地区做目录别名业务里 import 来自 district 即当前地区配置 district: path.join(__dirname, src/district, process.env.DISTRICT) } }, plugins: [ // 将 process.env 中列出的 key 在编译时注入到业务代码中 // 业务代码中 process.env.UNI_PLATFORM 等会被替换为构建时的常量 new webpack.EnvironmentPlugin([ UNI_PLATFORM, CLIENT_TYPE, VERSION, DISTRICT, API_BASE_URL, CUSTOM_TAB ]) ] } }说明require(./build/preBuild.js)保证在任何 webpack 配置使用process.env.DISTRICT、process.env.API_BASE_URL等之前这些变量就已经就绪。district别名指向src/district/${DISTRICT}便于按地区维护配置如src/district/anhui/config.js业务侧统一从district引用。EnvironmentPluginWebpack 内置插件会把数组中列出的process.env.XXX在编译时替换为当前构建时的值因此业务里写process.env.UNI_PLATFORM、process.env.DISTRICT、process.env.API_BASE_URL等即可无需再传参。3.5 按地区动态生成 pages.jsonbuild/pages.json.js不同地区可能需要不同的页面列表、tabBar 等因此把「基础配置」与「地区配置」合并后再写入src/pages.json。build/pages.json.jsconst fs require(fs) const path require(path) const lodash require(lodash) const standardPages require(../config/pages.js) let districtPage {} let customPages [] const district process.env.DISTRICT // 若存在该地区的 pages 配置则合并 try { districtPage require(../config/districts/${district}/pages.js) } catch (e) { console.log(不存在【${district}】地区差异化 page.js略过) } // 若有 CUSTOM_TAB可再合并 custom.xxx.pages.js process.env.CUSTOM_TAB.split(,).forEach((type) { // ... 按 type 加载 config/custom.${type}.pages.js 与 config/districts/${district}/ 下同名文件 }) const pageJSON lodash.mergeWith({}, standardPages, districtPage, ...customPages, (objValue, srcValue, key) { if (Array.isArray(objValue) key list) { return srcValue // 如 tabBar.list 用地区配置覆盖而不是数组合并 } }) fs.writeFileSync(path.join(__dirname, ../src/pages.json), JSON.stringify(pageJSON, null, 2), { encoding: utf8 }) console.log(page.json 构建完成)思路对基础 pages 地区 pages 自定义 tab 配置做 merge对list类数组采用覆盖策略避免 tab 等被意外合并。3.6 按地区动态生成 manifest.jsonbuild/manifest.json.js小程序与 H5 的 appid、描述等可能按地区不同同样采用「标准 manifest 地区 manifest」合并。build/manifest.json.jsconst fs require(fs) const path require(path) const lodash require(lodash) const standardManifest { name: app, appid: xxx, mp-weixin: { /* ... */ }, mp-alipay: { /* ... */ } // ... } let districtManifest {} try { districtManifest require(../config/districts/${process.env.DISTRICT}/manifest.json.js) } catch (e) { console.log(该地区不存在差异化 manifest.json.js) } const merged lodash.merge({}, standardManifest, districtManifest) fs.writeFileSync(path.join(__dirname, ../src/manifest.json), JSON.stringify(merged, null, 4), { encoding: utf8 }) console.log(manifest.json 生成完成)地区目录示例config/districts/anhui/manifest.json.js、config/districts/xinjiang/pages.js等按需添加。3.7 业务代码中如何使用构建时环境变量已被注入业务中直接读process.env即可。按平台分支// 例如仅微信小程序展示某模块 if (process.env.UNI_PLATFORM mp-weixin) { // 微信逻辑 } // 计算属性中 moreModuleGroups: vm process.env.UNI_PLATFORM mp-weixin ? xxxx : xxxx按地区使用配置// 使用别名 district实际指向 src/district/${DISTRICT} import banner from district/banner.jpg // 或直接使用注入的常量 const district process.env.DISTRICT const apiBase process.env.API_BASE_URL请求 API// 封装请求时用 process.env.API_BASE_URL 作为 baseURL axios.create({ baseURL: process.env.API_BASE_URL })四、目录与脚本约定小结角色路径/命令作用环境变量注入cross-env UNI_PLATFORM... DISTRICT... DEPLOY...在 npm script 中传入维度映射配置config/preBuild.config.js平台/地区/环境 → 客户端类型、标题、API构建前逻辑build/preBuild.js设置 CLIENT_TYPE、API_BASE_URL 等并生成 pages/manifestWebpack 入口vue.config.jsrequire preBuild、配置 district、EnvironmentPlugin动态页面配置build/pages.json.js合并基础 地区 pages写回 src/pages.json动态 manifestbuild/manifest.json.js合并基础 地区 manifest写回 src/manifest.json地区前端配置src/district/DISTRICT/config.js业务通过 district 引用地区构建配置config/districts/DISTRICT/pages.js、manifest.json.js仅该地区生效的页面与 manifest五、新增地区/环境/平台时的操作清单1.新增地区在config/preBuild.config.js的DISTRICT_CONFIG_MAP中增加APP_TITLE等。如需差异化页面在config/districts/新地区/下增加pages.js。如需差异化 manifest在config/districts/新地区/下增加manifest.json.js。在src/district/下新增同名目录及config.js可参考现有地区。2.新增部署环境在config/preBuild.config.js的API_BASE_URL_MAP中增加DEPLOY→ API 地址。在package.json的 scripts 中增加对应dev/build:平台:地区:环境命令。3.新增平台在CLIENT_TYPE_MAP中增加平台与客户端类型。若 uniapp 支持该平台只需在 script 中增加UNI_PLATFORM新平台的 dev/build 脚本即可。六、注意事项EnvironmentPlugin 与 preBuild 一致凡在preBuild.js里新加的process.env.XXX若要在业务代码中使用需在vue.config.js的EnvironmentPlugin数组中增加XXX。DISTRICT / DEPLOY 默认值preBuild 中使用了process.env.DISTRICT || xuzhou、process.env.DEPLOY || dev未传时会有默认地区与环境可按需修改。跨平台兼容使用cross-env可避免在 Windows 与 Mac/Linux 下环境变量写法不一致的问题。按上述方案即可用「地区 平台 环境」三维度通过一条命令完成打包并实现配置集中、扩展清晰欢迎大家学习指正