本文记录了将 3 个 uni-app (Vue2) 小程序从 HBuilderX 可视化项目升级为 CLI 项目,并实现一条命令完成「多客户 × 多项目 × 多平台」自动化构建发布的完整实战过程。包括 package.json 依赖配置的详细说明、9 个真实踩坑记录、多客户配置管理方案、以及微信小程序自动上传等。
一、项目背景
1.1 系统概况
我们是一个 B 端 SaaS 平台,前端有 4 个独立的 Web 项目:
| 项目 | 类型 | 说明 |
|---|---|---|
| 管理端小程序 | uni-app Vue2 | 面向管理人员的业务操作端 |
| 订货端小程序 | uni-app Vue2 | 面向采购方的订货端 |
| 供货端小程序 | uni-app Vue2 | 面向供应商的供货端 |
| 数据看板 | Vue2 + Webpack | 数据展示平台(纯 H5) |
3 个小程序最初是微信原生小程序 ,后来迁移到了 uni-app (Vue2),使用 HBuilderX 进行日常开发。迁移后一套代码可以同时产出 H5 和微信小程序,但构建发布仍然依赖 HBuilderX 的可视化操作。
1.2 面临的痛点
平台服务着 50+ 客户,每个客户都有独立的品牌、域名、Logo 和微信小程序 AppID。每次版本更新,发布流程是这样的:
- 打开 HBuilderX,手动修改
config.json(API 地址、邀请码、Logo 路径) - 修改
pages.json(应用标题) - 修改
manifest.json(小程序 AppID) - 点击 HBuilderX 菜单构建
- 打开微信开发者工具上传
- 改回配置,换下一个客户,重复以上步骤
50 个客户 × 3 个小程序 = 150 次手动操作,每次版本更新都是噩梦。
更致命的是:HBuilderX 只有 Windows 和 macOS 版本,Linux 服务器上无法构建,彻底堵死了 CI/CD 自动化的路。
1.3 改造目标
- HBuilderX 可视化项目 → CLI 项目,支持
npm run build命令行构建 - 实现一条命令发布:自动替换客户配置 → 构建 → 产物输出 → 还原配置
- 支持微信小程序通过
miniprogram-ci自动上传到微信公众平台 - Windows 和 Linux 双平台兼容
- 不影响现有 HBuilderX 开发流程(团队成员继续用 HBuilderX 开发调试)
二、核心改造:HBuilderX 项目升级为 CLI 项目
这是整篇文章的核心章节,也是踩坑最多的部分。
2.1 uni-app 的两种项目模式
uni-app 项目有两种模式,理解这个区别是改造的前提:
| 对比维度 | HBuilderX 可视化模式 | CLI 命令行模式 |
|---|---|---|
| 项目创建 | HBuilderX 菜单创建 | vue create -p dcloudio/uni-preset-vue |
| 源码目录 | 项目根目录(./) |
src/ 目录 |
| 构建方式 | HBuilderX 菜单「发行」 | npm run build:xxx |
| 配置管理 | HBuilderX 内置隐式处理 | 需要显式配置 package.json、babel、postcss 等 |
| 依赖管理 | HBuilderX 内置依赖,无需 node_modules | 需要 npm install 安装所有依赖 |
| Linux 支持 | 不支持 | 支持 |
关键认知: HBuilderX 模式下,package.json 中只需要声明少量业务依赖(如 uni-ui、二维码库等),@dcloudio 全家桶、Babel、PostCSS、Webpack 等编译工具链全部内置在 IDE 中 ,不需要出现在 package.json 里,也不需要 node_modules。改造为 CLI 模式,就是要把这些「隐式内置」的编译依赖全部显式声明到 package.json 中。
2.2 改造约束:两种模式必须共存
我们的改造有一个硬约束:
改造后,团队成员继续用 HBuilderX 打开项目进行开发和调试,完全不受影响。
这意味着不能用 vue create 重新初始化项目(那会改变目录结构为 src/),而是在现有 HBuilderX 项目上叠加 CLI 构建能力。
2.3 构建命令设计
在 package.json 的 scripts 中定义两个构建命令:
json
{
"scripts": {
"build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 UNI_INPUT_DIR=./ vue-cli-service uni-build",
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin UNI_INPUT_DIR=./ vue-cli-service uni-build"
}
}
逐个参数解析:
| 环境变量 | 值 | 作用 |
|---|---|---|
NODE_ENV |
production |
生产模式,启用代码压缩、Tree Shaking 等优化 |
UNI_PLATFORM |
h5 或 mp-weixin |
指定目标平台,决定编译产物类型 |
UNI_INPUT_DIR |
./ |
指定源码目录为项目根目录 (CLI 默认从 src/ 读取,但 HBuilderX 项目源码在根目录,不设置会报 Cannot find module 'src/manifest.json') |
构建命令 vue-cli-service uni-build 是 @dcloudio/vue-cli-plugin-uni 注册的自定义命令,它会读取 manifest.json 和 pages.json,调用 webpack 进行编译。
构建产物路径:
- H5:
dist/build/h5/ - 微信小程序:
dist/build/mp-weixin/
2.4 完整 package.json 依赖配置详解
这是最关键的部分,每个依赖都有明确的用途,下面是完整配置和逐项说明。
2.4.1 完整 package.json
json
{
"name": "my-miniprogram",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 UNI_INPUT_DIR=./ vue-cli-service uni-build",
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin UNI_INPUT_DIR=./ vue-cli-service uni-build"
},
"dependencies": {
"@dcloudio/uni-app": "2.0.2-4070620250821001",
"@dcloudio/uni-app-plus": "2.0.2-4070620250821001",
"@dcloudio/uni-cli-i18n": "2.0.2-4070620250821001",
"@dcloudio/uni-cli-shared": "2.0.2-4070620250821001",
"@dcloudio/uni-console": "3.0.0-4070620250821001",
"@dcloudio/uni-h5": "2.0.2-4070620250821001",
"@dcloudio/uni-i18n": "2.0.2-4070620250821001",
"@dcloudio/uni-migration": "2.0.2-4070620250821001",
"@dcloudio/uni-mp-weixin": "2.0.2-4070620250821001",
"@dcloudio/uni-stat": "2.0.2-4070620250821001",
"@dcloudio/uni-template-compiler": "2.0.2-4070620250821001",
"@dcloudio/uni-ui": "^1.5.11",
"@dcloudio/webpack-uni-mp-loader": "2.0.2-4070620250821001",
"@dcloudio/webpack-uni-pages-loader": "2.0.2-4070620250821001",
"regenerator-runtime": "^0.14.1",
"vue": "^2.7.14",
"vue-template-compiler": "^2.7.14"
},
"devDependencies": {
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@dcloudio/vue-cli-plugin-hbuilderx": "2.0.2-4070620250821001",
"@dcloudio/vue-cli-plugin-uni": "2.0.2-4070620250821001",
"@dcloudio/vue-cli-plugin-uni-optimize": "2.0.2-4070620250821001",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"autoprefixer": "^9.8.8",
"cross-env": "^7.0.3",
"postcss": "^8.4.38",
"sass": "^1.97.3"
}
}
2.4.2 dependencies 逐项说明
@dcloudio 核心包(版本必须全部一致:2.0.2-4070620250821001):
| 包名 | 作用 |
|---|---|
@dcloudio/uni-app |
uni-app 核心运行时 ,提供 uni.* API、生命周期钩子、页面路由等基础能力 |
@dcloudio/uni-app-plus |
App 端运行时(虽然我们暂时只用 H5 和微信小程序,但 uni-app 编译时需要它) |
@dcloudio/uni-h5 |
H5 平台编译支持,将 uni-app 组件和 API 编译为 Web 标准代码 |
@dcloudio/uni-mp-weixin |
微信小程序平台编译支持,将 uni-app 编译为微信小程序原生代码 |
@dcloudio/uni-stat |
uni 统计模块,收集应用启动、页面访问等数据 |
@dcloudio/uni-template-compiler |
模板编译器 ,将 .vue 文件的 <template> 编译为各平台的渲染代码 |
@dcloudio/uni-cli-i18n |
CLI 国际化支持,构建过程中的提示信息多语言 |
@dcloudio/uni-cli-shared |
CLI 共享工具,编译流程中各模块共享的工具函数 |
@dcloudio/uni-console |
控制台增强,统一各平台的 console 输出格式 |
@dcloudio/uni-i18n |
运行时国际化,提供应用内多语言支持 |
@dcloudio/uni-migration |
迁移辅助工具,帮助处理不同版本间的 API 差异 |
@dcloudio/webpack-uni-mp-loader |
Webpack Loader,处理小程序平台的文件转换(.vue → .wxml/.wxss/.js) |
@dcloudio/webpack-uni-pages-loader |
Webpack Loader ,解析 pages.json 生成路由配置和入口文件 |
其他依赖:
| 包名 | 版本 | 作用 |
|---|---|---|
@dcloudio/uni-ui |
^1.5.11 |
uni-app 官方组件库(弹窗、列表、表单等) |
vue |
^2.7.14 |
Vue 2.x 运行时(uni-app Vue2 版本基于 Vue 2.7) |
vue-template-compiler |
^2.7.14 |
Vue 模板编译器 ,版本必须与 vue 完全一致,否则编译报错 |
regenerator-runtime |
^0.14.1 |
async/await 运行时,为不支持 Generator 的环境提供 polyfill |
2.4.3 devDependencies 逐项说明
@dcloudio CLI 插件(版本与 dependencies 中的 @dcloudio 包一致):
| 包名 | 作用 |
|---|---|
@dcloudio/vue-cli-plugin-uni |
uni-app 的 Vue CLI 插件核心 ,注册 vue-cli-service uni-build 命令,配置 webpack 编译规则 |
@dcloudio/vue-cli-plugin-uni-optimize |
编译优化插件,Tree Shaking、代码分割、按需编译等 |
@dcloudio/vue-cli-plugin-hbuilderx |
HBuilderX 兼容插件,让 CLI 模式能正确处理 HBuilderX 创建的项目结构(如根目录源码、特殊配置) |
Vue CLI 核心(版本 ~4.5.0):
| 包名 | 版本 | 作用 |
|---|---|---|
@vue/cli-service |
~4.5.0 |
Vue CLI 核心服务 ,提供 webpack 配置和 vue-cli-service 命令。版本锁定 4.5.x 是因为 uni-app Vue2 版本与 Vue CLI 4.x 配套,不支持 5.x |
@vue/cli-plugin-babel |
~4.5.0 |
Babel 集成插件,配置 webpack 的 babel-loader,版本必须与 cli-service 匹配 |
Babel 插件:
| 包名 | 版本 | 作用 |
|---|---|---|
@babel/plugin-proposal-optional-chaining |
^7.21.0 |
支持可选链语法 ?.(如 obj?.a?.b)。HBuilderX 内置支持此语法,但 CLI 模式的 webpack + babel 需要显式配置 |
@babel/plugin-proposal-nullish-coalescing-operator |
^7.18.6 |
支持空值合并运算符 ??(如 val ?? 'default')。同上,HBuilderX 内置但 CLI 需要显式配置 |
构建工具链:
| 包名 | 版本 | 作用 | 为什么选它 |
|---|---|---|---|
cross-env |
^7.0.3 |
跨平台设置环境变量 。Windows 用 set VAR=value,Linux 用 VAR=value,cross-env 统一为一种写法 |
脚本需要同时在 Windows 和 Linux 运行 |
sass |
^1.97.3 |
Sass CSS 预处理器 (dart-sass 实现)。编译 .scss 文件 |
替代 node-sass。node-sass 是 C++ 原生模块,Linux 上安装需要 Python 和编译器,经常失败。dart-sass 是纯 JS 实现,安装零门槛 |
postcss |
^8.4.38 |
CSS 后处理器,用于 autoprefixer 等插件 | CLI 模式需要显式安装,HBuilderX 内置 |
autoprefixer |
^9.8.8 |
自动添加 CSS 浏览器前缀 (-webkit-、-moz- 等) |
版本锁定 9.x 是因为 10.x 要求 PostCSS 8,而 uni-app 内部部分依赖仍使用 PostCSS 7 |
2.5 版本匹配------最关键的事
整个改造中最容易出问题的就是版本不匹配。以下是版本匹配关系图:
HBuilderX 4.76
↕ 必须对应
@dcloudio/* 2.0.2-4070620250821001
↕ 配套使用
@vue/cli-service ~4.5.0 ← uni-app Vue2 绑定 Vue CLI 4.x
↕ 版本一致
@vue/cli-plugin-babel ~4.5.0
↕ 配套使用
vue ^2.7.14 ←→ vue-template-compiler ^2.7.14 ← 这两个必须版本完全一致
@dcloudio 版本号 2.0.2-4070620250821001 的含义:
2.0.2是 uni-app 的大版本号4070620250821001是 HBuilderX 的内部版本标识(对应 HBuilderX 4.76)
当 HBuilderX 升级时 ,必须同步更新 package.json 中所有 @dcloudio 包的版本号。否则会出现 HBuilderX 开发预览正常,但 CLI 构建产物行为不一致的问题。
查找 @dcloudio 版本号的方法: 打开 HBuilderX → 菜单「帮助」→「关于」可以看到版本号。然后到 npm 上搜索
@dcloudio/uni-app,找到对应 HBuilderX 版本的包版本。
2.6 Babel 配置
在项目根目录创建 babel.config.js:
javascript
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator'
]
}
为什么 HBuilderX 不需要这个文件?
HBuilderX 的内置编译器已经集成了这些 Babel 插件。但 CLI 模式使用的是 @vue/cli-service + webpack + babel-loader 的标准流程,需要显式声明。
如果不配置,项目中任何使用 ?. 或 ?? 的代码都会报 SyntaxError: Unexpected token。
2.7 安装依赖
bash
npm install --legacy-peer-deps
为什么需要 --legacy-peer-deps?
Vue 2.7 内置了 Composition API(@vue/composition-api),但部分 @dcloudio 包的 peerDependencies 还声明了对独立的 @vue/composition-api 包的依赖。npm 7+ 默认严格检查 peer deps,会报 ERESOLVE 冲突错误。--legacy-peer-deps 回退到 npm 6 的行为,忽略 peer deps 冲突。
2.8 验证构建
bash
# 构建 H5 版本
npm run build:h5
# 成功后产物在 dist/build/h5/
# 构建微信小程序版本
npm run build:mp-weixin
# 成功后产物在 dist/build/mp-weixin/
2.9 踩坑全记录(按时间顺序)
改造过程并不顺利,以下是我们遇到的全部坑,按出现顺序排列:
坑 1:Cannot find module 'src/manifest.json'
ERROR Error: Cannot find module '/project/src/manifest.json'
- 原因 :CLI 模式默认从
src/目录查找源码,而 HBuilderX 项目的源码(manifest.json、pages.json、App.vue)都在项目根目录 - 解决 :在构建命令中添加
UNI_INPUT_DIR=./环境变量
diff
- "build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build"
+ "build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 UNI_INPUT_DIR=./ vue-cli-service uni-build"
坑 2:Cannot find module 'node-sass'
Module build failed: Error: Cannot find module 'node-sass'
- 原因 :项目中使用了 SCSS 语法(
<style lang="scss">),HBuilderX 内置了 Sass 编译器,但 CLI 模式需要在 node_modules 中安装 - 为什么不装 node-sass :node-sass 是 C++ 原生模块,Linux 上安装需要
python、make、g++等编译工具,经常因为缺少依赖或 Node.js 版本不匹配而安装失败 - 解决 :安装
sass(dart-sass),纯 JavaScript 实现,跨平台零依赖
bash
npm install --save-dev sass
坑 3:No PostCSS Config found
Error: No PostCSS Config found in: /project/static/
- 原因 :CLI 模式的 webpack 使用
postcss-loader处理 CSS,需要找到 PostCSS 配置文件 - 解决 :安装
postcss和autoprefixer,在 package.json 的 devDependencies 中声明即可。uni-app 的 CLI 插件会自动检测到这些依赖并配置 postcss-loader
bash
npm install --save-dev postcss autoprefixer
注意 :autoprefixer 版本用
^9.8.8而不是最新的 10.x,因为 autoprefixer 10 要求 PostCSS 8,但 uni-app 内部部分 loader 仍依赖 PostCSS 7,混用可能导致冲突。
坑 4 & 5:SyntaxError: Unexpected token '?.' 和 Unexpected token '??'
Module build failed: SyntaxError: Unexpected token (123:45)
121 | const value = obj?.nested?.property
- 原因 :项目代码中使用了 ES2020 的可选链(
?.)和空值合并(??)语法。HBuilderX 内置的编译器支持这些语法,但 CLI 模式的 Babel 默认不转换 - 解决:安装并配置 Babel 插件
bash
npm install --save-dev @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-nullish-coalescing-operator
在 babel.config.js 中添加 plugins(见 2.6 节)。
坑 6:第三方依赖中的 ?. 语法报错
node_modules/@dcloudio/xxx/lib/configLoader.js:25
const result = config?.options?.compiler
SyntaxError: Unexpected token '.'
- 原因 :node_modules 中的第三方依赖代码也使用了可选链语法,但 Babel 默认不处理
node_modules目录(通过exclude: /node_modules/跳过) - 解决 :手动修改报错的文件,将
?.改写为传统的&&链式判断
javascript
// 修改前
const result = config?.options?.compiler
// 修改后
const result = config && config.options && config.options.compiler
这是无奈之举。更好的做法是升级 Node.js 到 14+(原生支持
?.),但我们的 Linux 服务器环境受限。
坑 7:Module not found: dist/modal/index.css
Module not found: Error: Can't resolve './dist/modal/index.css'
- 原因 :某些
uni_modules下的第三方组件在代码中引用了 CSS 文件(@import './dist/modal/index.css'),但实际文件不存在。HBuilderX 编译时对缺失的 CSS import 做了静默处理,但 CLI 模式的 webpack 会严格报错 - 解决:创建空的 CSS 占位文件
bash
mkdir -p uni_modules/xxx-component/dist/modal/
touch uni_modules/xxx-component/dist/modal/index.css
坑 8:npm install ERESOLVE 依赖冲突
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! peer dep missing: @vue/composition-api@"^1.0.0"
- 原因 :Vue 2.7 已内置 Composition API,不需要也不应该安装独立的
@vue/composition-api包。但部分@dcloudio包的 peerDependencies 还声明了对它的依赖 - 解决 :使用
--legacy-peer-deps参数
bash
npm install --legacy-peer-deps
坑 9:Linux 下文件名大小写问题
Module not found: Error: Can't resolve './components/Footer'
# 实际文件名是 footer.vue(小写 f)
- 原因 :Windows 文件系统不区分大小写(
Footer.vue和footer.vue指向同一个文件),但 Linux 严格区分。在 Windows 上开发时写了import Footer from './Footer',实际文件名是footer.vue,Windows 上正常运行,到 Linux 上就报错了 - 解决:逐个修复 import 路径,确保大小写与实际文件名完全一致。这个问题在我们的数据看板项目中一共发现了 6 处
预防建议 :在
.gitattributes中设置* text=auto,或使用 ESLint 的import/no-unresolved规则检查路径。
2.10 改造前后对比
项目目录结构对比
改造前(纯 HBuilderX 项目):
SmallProgram_uni/
├── .hbuilderx/ # HBuilderX 编辑器配置
├── common/ # 公共模块
├── components/ # 组件
├── data/ # 静态数据
├── js_sdk/ # JS SDK
├── pages/ # 页面
├── static/ # 静态资源(Logo、图片等)
├── uni_modules/ # uni-app 插件市场组件
├── unpackage/ # HBuilderX 构建产物
├── utils/ # 工具函数
├── wxs/ # WXS 脚本
├── App.vue # 应用入口组件
├── main.js # 应用入口 JS
├── manifest.json # uni-app 应用配置
├── pages.json # 页面路由配置
├── uni.scss # 全局 SCSS 变量
├── index.html # H5 入口模板
├── package.json # 仅声明业务依赖(uni-ui、二维码等 6 个包)
├── project.config.json # 微信小程序项目配置
├── project.private.config.json
├── androidPrivacy.json # Android 隐私政策
├── sitemap.json # 微信小程序 sitemap
└── README.md
📌 特征 :虽然有
package.json,但里面只有 6 个业务依赖 (uni-ui、二维码扫描、cookie 等),没有scripts构建命令、没有devDependencies开发依赖、没有node_modules/。所有编译能力由 HBuilderX 编辑器内置提供,项目自身不具备独立构建能力。
改造后(HBuilderX + CLI 双模式项目):
SmallProgram_uni/
├── .hbuilderx/ # ← 保留,HBuilderX 开发不受影响
├── common/
├── components/
├── data/
├── js_sdk/
├── pages/
├── static/
├── uni_modules/
├── unpackage/
├── utils/
├── wxs/
├── App.vue
├── main.js
├── manifest.json
├── pages.json
├── uni.scss
├── index.html
├── project.config.json
├── project.private.config.json
├── androidPrivacy.json
├── sitemap.json
├── README.md
│
│── 📝 package.json # 【大幅改造】新增 scripts + devDependencies + 13 个 @dcloudio 运行时包
│── ✅ package-lock.json # 【新增】依赖锁定文件(745KB)
│── ✅ babel.config.js # 【新增】Babel 转译配置(?. ?? 语法支持)
│── ✅ vue.config.js # 【新增】Vue CLI 配置(transpileDependencies)
│── ✅ node_modules/ # 【新增】npm 依赖(构建时安装)
│── ✅ clients.json # 【新增】多客户配置文件
│── ✅ dist/ # 【新增】CLI 构建产物目录
└── ✅ app.css # 【新增】编译生成的全局样式
📌 特征 :新增了 6 个文件/目录 ,
package.json从仅有业务依赖大幅改造为完整的 CLI 构建配置。原有业务文件零修改,HBuilderX 打开项目后一切照旧。
package.json 改造前后对比
这是整个 CLI 改造中变化最大的文件,我们来看看改造前后的完整差异:
改造前(仅 6 个业务依赖,无构建能力):
json
{
"name": "SmallProgram",
"version": "1.0.0",
"main": "main.js",
"dependencies": {
"@dcloudio/uni-i18n": "^2.0.2-4050720250324001",
"@dcloudio/uni-ui": "^1.5.11",
"@zxing/library": "^0.21.3",
"compressorjs": "^1.2.1",
"html5-qrcode": "^2.3.8",
"weapp-cookie": "^1.4.8"
}
}
改造后(完整 CLI 构建能力):
json
{
"name": "SmallProgram",
"version": "1.0.0",
"main": "main.js",
"scripts": { // ← 【新增】构建命令
"build:h5": "cross-env ... UNI_INPUT_DIR=./ vue-cli-service uni-build",
"build:mp-weixin": "cross-env ... UNI_INPUT_DIR=./ vue-cli-service uni-build"
},
"dependencies": {
"@dcloudio/uni-app": "2.0.2-4070620250821001", // ← 【新增】13 个 @dcloudio 运行时包
"@dcloudio/uni-app-plus": "2.0.2-4070620250821001",
"@dcloudio/uni-cli-i18n": "2.0.2-4070620250821001",
"@dcloudio/uni-cli-shared": "2.0.2-4070620250821001",
"@dcloudio/uni-console": "3.0.0-4070620250821001",
"@dcloudio/uni-h5": "2.0.2-4070620250821001",
"@dcloudio/uni-i18n": "2.0.2-4070620250821001", // ← 【升级】从 ^2.0.2-405... 到精确版本
"@dcloudio/uni-migration": "2.0.2-4070620250821001",
"@dcloudio/uni-mp-weixin": "2.0.2-4070620250821001",
"@dcloudio/uni-stat": "2.0.2-4070620250821001",
"@dcloudio/uni-template-compiler": "2.0.2-4070620250821001",
"@dcloudio/uni-ui": "^1.5.11",
"@dcloudio/webpack-uni-mp-loader": "2.0.2-4070620250821001",
"@dcloudio/webpack-uni-pages-loader": "2.0.2-4070620250821001",
"regenerator-runtime": "^0.14.1", // ← 【新增】async/await 运行时
"@zxing/library": "^0.21.3", // ← 【保留】原业务依赖
"compressorjs": "^1.2.1",
"html5-qrcode": "^2.3.8",
"vue": "^2.7.14", // ← 【新增】显式声明 Vue
"vue-template-compiler": "^2.7.14", // ← 【新增】模板编译器
"weapp-cookie": "^1.4.8"
},
"devDependencies": { // ← 【新增】整个区块
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@dcloudio/vue-cli-plugin-hbuilderx": "2.0.2-4070620250821001",
"@dcloudio/vue-cli-plugin-uni": "2.0.2-4070620250821001",
"@dcloudio/vue-cli-plugin-uni-optimize": "2.0.2-4070620250821001",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"autoprefixer": "^9.8.8",
"cross-env": "^7.0.3",
"postcss": "^8.4.38",
"sass": "^1.97.3"
}
}
量化对比:
| 对比项 | 改造前 | 改造后 | 变化 |
|---|---|---|---|
scripts |
无 | 2 个构建命令 | 从 0 到有 |
dependencies 数量 |
6 个 | 21 个 | +15 个(@dcloudio 运行时 + Vue + regenerator) |
devDependencies 数量 |
0 个 | 11 个 | 从 0 到有(CLI 工具链) |
| 总依赖数 | 6 个 | 32 个 | +26 个 |
@dcloudio 包 |
2 个 | 14 个 | +12 个(HBuilderX 内置的现在要显式声明) |
新增文件说明
| 新增/改造文件 | 大小 | 作用 |
|---|---|---|
package.json 📝 |
1.9KB | 改造:新增 scripts + devDependencies + 13 个 @dcloudio 运行时包 |
package-lock.json ✅ |
745KB | 锁定所有依赖的精确版本,确保团队一致性 |
babel.config.js ✅ |
205B | 配置 Babel 预设和 ?. ?? 语法转译插件 |
vue.config.js ✅ |
69B | 配置 transpileDependencies: ['@dcloudio/uni-ui'] |
node_modules/ ✅ |
~120MB | npm 安装的依赖包(已加入 .gitignore) |
clients.json ✅ |
21KB | 50+ 客户的多平台配置(baseUrl、appid 等) |
dist/ ✅ |
变动 | CLI 构建产物目录(HBuilderX 用 unpackage/) |
版本匹配关系
改造成功的关键在于版本号的精确匹配,以下是最终确认可用的版本组合:
| 组件 | 版本号 | 说明 |
|---|---|---|
| HBuilderX | 4.76 (2025.08.21) | 编辑器版本,决定 @dcloudio 包版本 |
| @dcloudio/* | 2.0.2-4070620250821001 | 13 个包全部统一此版本(版本号末 8 位 = HBuilderX 发布日期) |
| Vue | 2.7.14 | Vue 2 最后一个稳定版本 |
| vue-template-compiler | 2.7.14 | 必须与 Vue 主版本完全一致 |
| @vue/cli-service | ~4.5.0 | Vue CLI 4.x(不能用 5.x) |
| @vue/cli-plugin-babel | ~4.5.0 | 与 CLI Service 同系列 |
| sass (dart-sass) | ^1.97.3 | 替代 node-sass,纯 JS 跨平台 |
| postcss | ^8.4.38 | CSS 后处理器 |
| autoprefixer | ^9.8.8 | CSS 前缀自动补全(9.x,非 10.x) |
| cross-env | ^7.0.3 | 跨平台环境变量设置 |
| Node.js | 14.x / 16.x | 推荐 Node 16,最低 Node 14 |
| npm | 6.x / 8.x | 与 Node.js 版本配套 |
⚠️ 版本号中的隐含规律 :
2.0.2-4070620250821001这个版本号的含义是2.0.2-{产品线编码}{YYYYMMDD}{构建序号},其中20250821对应 HBuilderX 的发布日期。升级 HBuilderX 后,需要同步更新 package.json 中所有@dcloudio包到新版本号。
改造前后能力对比
| 能力维度 | 改造前(纯 HBuilderX) | 改造后(CLI + HBuilderX) |
|---|---|---|
| 构建方式 | 只能通过 HBuilderX GUI 菜单构建 | npm run build:h5 / npm run build:mp-weixin 命令行构建 |
| 运行平台 | 仅 Windows / macOS(HBuilderX 限制) | Windows / macOS / Linux 服务器 |
| CI/CD 集成 | ❌ 无法实现 | ✅ Shell 脚本 / Jenkins / GitHub Actions |
| 自动化发布 | ❌ 每个客户手动操作 | ✅ 一条命令发布全部客户 |
| 多客户支持 | 手动改配置 → 构建 → 改回(每客户 5 分钟) | 脚本自动替换 → 构建 → 还原(每客户 30 秒) |
| 微信上传 | 打开微信开发者工具手动上传 | miniprogram-ci 命令行自动上传 |
| 日常开发 | HBuilderX 热重载开发 | 不受影响,HBuilderX 照常使用 |
| 团队协作 | 只有装了 HBuilderX 的成员能构建 | 任何有 Node.js 环境的成员都能构建 |
2.11 改造总结
经过以上改造,3 个小程序项目都能通过 npm run build:h5 和 npm run build:mp-weixin 在命令行完成构建,同时在 HBuilderX 中打开和运行完全不受影响。
改造的本质是一句话:把 HBuilderX 内置的「隐式配置」全部变成 package.json 中的「显式依赖」。
三、多客户配置管理
3.1 需求分析
50+ 客户,每个客户需要独立的配置,且 H5 和微信小程序的配置还不一样:
| 配置项 | H5 | 微信小程序 | 说明 |
|---|---|---|---|
| baseUrl | ✅ | ✅ | API 后端地址,每个客户不同 |
| inviteCode | ✅ | ✅ | 客户标识码,H5 通常用通用值,小程序每个客户独立 |
| logoFolder | ✅ | ✅ | Logo 文件夹名 |
| title | ✅ | ✅ | 应用标题(显示在导航栏) |
| appid | ❌ | ✅ | 微信小程序 AppID,仅小程序需要 |
| navigateToMiniProgramAppIdList | ❌ | ✅ | 小程序跳转白名单,仅小程序需要 |
3.2 配置文件设计:每项目一个 clients.json
方案对比:
| 方案 | 文件数 | 优势 | 劣势 |
|---|---|---|---|
| 每客户独立 JSON | 160 个 | 单客户修改不影响其他 | 文件数量爆炸 |
| 每项目一个汇总文件 | 4 个 | 结构清晰,易维护 | 单文件较大 |
| 数据库存储 | 0 个 | 动态修改 | 增加系统复杂度 |
我们选择了每项目一个 clients.json,按平台分区:
json
{
"customer_a": {
"name": "客户A",
"uniAppid": "__UNI__XXXXXXX",
"h5": {
"title": "XX管理端",
"baseUrl": "https://api.customer-a.com/api",
"inviteCode": "common_h5",
"logoFolder": "common_h5"
},
"mp-weixin": {
"title": "客户A管理端",
"baseUrl": "https://api.customer-a.com/api",
"inviteCode": "customer_a",
"logoFolder": "customer_a",
"appid": "wx1234567890abcdef"
}
},
"customer_b": {
"name": "客户B",
"h5": {
"title": "XX管理端",
"baseUrl": "https://api.customer-b.com/api"
}
}
}
按平台分区的好处:构建脚本只需要根据目标平台(h5 / mp-weixin)直接取对应节点的配置,逻辑简洁。
3.3 配置数据来源
50+ 客户的配置维护在 Excel 模板中(非技术同事也能编辑),通过 Python 脚本解析生成 JSON:
Excel 模板 → Python 脚本解析 → clients.json(4 个文件)
最终生成:数据看板 34 个 + 管理端 50 个 + 订货端 38 个 + 供货端 38 个 = 160 个客户配置。
四、自动化发布脚本
4.1 整体架构
publish-frontend.sh 一键发布脚本
|
┌──────────┴──────────┐
▼ ▼
H5 发布 微信小程序发布
(构建→复制) (构建→瘦身→上传)
|
miniprogram-ci
自动上传到微信
4.2 脚本整体结构
publish-frontend.sh(约 600 行),纯参数化调用,不包含任何交互逻辑:
bash
# 用法
./scripts/publish-frontend.sh \
--customer customer_a \
--projects manage,purchase,supplier \
--type h5
脚本顶部先定义各项目的路径、构建命令、产物目录等映射表:
bash
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# 项目路径映射
declare -A PROJECT_PATHS=(
["databoard"]="webs/databoardweb/CCA.Databoard.Views"
["manage"]="webs/manageweb/Wx_APP.SmallProgram_uni"
["purchase"]="webs/pursupweb/NewPrjt/PurchaseSmallProgram_uni"
["supplier"]="webs/pursupweb/NewPrjt/SupplierSmallProgram_uni"
)
# H5 构建命令(驾驶舱是 Vue2+Webpack 项目,命令不同)
declare -A H5_BUILD_CMDS=(
["databoard"]="npm run build--prod"
["manage"]="npm run build:h5"
["purchase"]="npm run build:h5"
["supplier"]="npm run build:h5"
)
# 微信小程序构建命令(驾驶舱不支持小程序)
declare -A MP_BUILD_CMDS=(
["manage"]="npm run build:mp-weixin"
["purchase"]="npm run build:mp-weixin"
["supplier"]="npm run build:mp-weixin"
)
# H5 产物目录(驾驶舱产物在 dist/,小程序在 dist/build/h5/)
declare -A H5_OUTPUT_DIRS=(
["databoard"]="dist"
["manage"]="dist/build/h5"
["purchase"]="dist/build/h5"
["supplier"]="dist/build/h5"
)
# 微信小程序产物目录
declare -A MP_OUTPUT_DIRS=(
["manage"]="dist/build/mp-weixin"
["purchase"]="dist/build/mp-weixin"
["supplier"]="dist/build/mp-weixin"
)
# 输出子目录名
declare -A H5_OUTPUT_SUBDIRS=(
["databoard"]="databoard-web"
["manage"]="manage-h5"
["purchase"]="purchase-h5"
["supplier"]="supplier-h5"
)
使用
declare -A关联数组做映射表的好处:新增项目只需要在映射表中加一行,不需要修改任何逻辑代码。
4.3 核心函数实现
对每个选中的项目,主流程依次调用以下函数:
bash
main() {
validate_customer "$CUSTOMER"
validate_projects "$PROJECTS"
IFS=',' read -ra PROJ_ARRAY <<< "$PROJECTS"
for proj in "${PROJ_ARRAY[@]}"; do
# 1. 检查依赖
check_dependencies "$proj"
# 2. 替换配置
if [ "$proj" = "databoard" ]; then
replace_databoard_config "$CUSTOMER"
else
replace_miniprogram_config "$CUSTOMER" "$proj"
fi
# 3. Logo 瘦身 + 排除大模块(仅微信小程序)
if [ "$proj" != "databoard" ]; then
logo_slimming "$proj" "$CUSTOMER"
exclude_uni_modules "$proj"
fi
# 4. 构建
if ! do_build "$proj"; then
restore_config "$proj"
echo "错误: 构建失败,已还原配置"
exit 1
fi
# 5. 复制产物(内含构建后清理)
copy_output "$proj" "$CUSTOMER"
# 6. 还原配置
restore_config "$proj"
done
}
注意第 4 步的错误处理:构建失败时也要先还原配置再退出,否则 Git 工作区会被污染。
4.3.1 依赖检查
bash
check_dependencies() {
local project="$1"
local project_path="$PROJECT_ROOT/${PROJECT_PATHS[$project]}"
if [ ! -d "$project_path/node_modules" ]; then
echo ">>> 安装依赖: $project"
cd "$project_path"
npm install --legacy-peer-deps
fi
}
4.3.2 小程序配置替换
这是最复杂的函数,需要替换 3 个配置文件,数据来源是 clients.json:
bash
replace_miniprogram_config() {
local customer="$1"
local project="$2"
local project_path="$PROJECT_ROOT/${PROJECT_PATHS[$project]}"
local clients_file="$project_path/clients.json"
# 用 Python 从 clients.json 中读取对应客户、对应平台的配置
local base_url=$(python3 -c "
import json
d = json.load(open('$clients_file'))
print(d['$customer']['$PUBLISH_TYPE']['baseUrl'])
")
local invite_code=$(python3 -c "
import json
d = json.load(open('$clients_file'))
print(d['$customer']['$PUBLISH_TYPE']['inviteCode'])
")
local logo_folder=$(python3 -c "
import json
d = json.load(open('$clients_file'))
print(d['$customer']['$PUBLISH_TYPE']['logoFolder'])
")
local title=$(python3 -c "
import json
d = json.load(open('$clients_file'))
print(d['$customer']['$PUBLISH_TYPE']['title'])
")
# ① 替换 static/config.json(直接覆写)
cat > "$project_path/static/config.json" << CFG
{
"baseUrl": "$base_url",
"inviteCode": "$invite_code",
"logoFolder": "$logo_folder"
}
CFG
# ② 替换 pages.json 中的标题
local pages_json="$project_path/pages.json"
if [ -n "$title" ]; then
sed -i "s/\"navigationBarTitleText\": \"[^\"]*\"/\"navigationBarTitleText\": \"$title\"/g" "$pages_json"
fi
# ③ 替换 manifest.json(仅微信小程序发布时)
if [ "$PUBLISH_TYPE" = "mp-weixin" ]; then
local manifest_json="$project_path/manifest.json"
local uni_appid=$(python3 -c "
import json
d = json.load(open('$clients_file'))
print(d['$customer']['uniAppid'])
")
sed -i "s/\"name\" : \"[^\"]*\"/\"name\" : \"$title\"/g" "$manifest_json"
sed -i "s/\"appid\" : \"__UNI__[^\"]*\"/\"appid\" : \"$uni_appid\"/g" "$manifest_json"
fi
echo ">>> 已替换 $project 配置"
}
为什么用 Python 读 JSON? Bash 本身没有原生的 JSON 解析能力。用
jq也可以,但 Python 在 Linux 上几乎是标配,不需要额外安装。
4.3.3 Logo 瘦身
微信小程序有 2MB 的主包体积限制 ,而 static/images/ 下有 50+ 个客户的 Logo 文件夹,总共几十 MB。
解决方案:构建前临时移走非当前客户的 Logo 文件夹,构建后还原。
bash
logo_slimming() {
local project="$1"
local customer="$2"
local project_path="$PROJECT_ROOT/${PROJECT_PATHS[$project]}"
local clients_file="$project_path/clients.json"
local images_dir="$project_path/static/images"
# 仅微信小程序需要瘦身
if [ "$PUBLISH_TYPE" != "mp-weixin" ]; then
return 0
fi
# 获取当前客户的 logo 文件夹名
local logo_folder=$(python3 -c "
import json
d = json.load(open('$clients_file'))
print(d['$customer']['$PUBLISH_TYPE']['logoFolder'])
")
# 备份并移走非当前客户的 Logo
mkdir -p "$project_path/.logo_backup"
if [ -d "$images_dir" ]; then
for dir in "$images_dir"/*/; do
if [ -d "$dir" ]; then
dir_name=$(basename "$dir")
if [ "$dir_name" != "$logo_folder" ]; then
mv "$dir" "$project_path/.logo_backup/"
fi
fi
done
fi
echo ">>> 已执行 Logo 瘦身: 保留 $logo_folder"
}
4.3.4 构建后清理(仅微信小程序)
即使做了 Logo 瘦身,构建产物中仍可能有不需要的大文件。这一步在产物目录(而非源码目录)中清理:
bash
post_build_clean() {
local project="$1"
local customer="$2"
local mp_dir="$project_path/dist/build/mp-weixin"
if [ "$PUBLISH_TYPE" != "mp-weixin" ] || [ ! -d "$mp_dir" ]; then
return 0
fi
# 1. 移除大型视频播放库(约 2MB,仅部分客户用到)
if [ -d "$mp_dir/uni_modules/sy-new-jessibuca" ]; then
rm -rf "$mp_dir/uni_modules/sy-new-jessibuca"
echo ">>> 已移除 jessibuca 视频库"
fi
# 2. 只保留当前客户的 logo 文件夹
local logo_folder=$(python3 -c "
import json; d=json.load(open('$clients_file'))
print(d.get('$customer',{}).get('$PUBLISH_TYPE',{}).get('logoFolder',''))
")
if [ -d "$mp_dir/static/images" ] && [ -n "$logo_folder" ]; then
for dir in "$mp_dir/static/images"/*/; do
dir_name=$(basename "$dir")
if [ "$dir_name" != "$logo_folder" ]; then
rm -rf "$dir"
fi
done
echo ">>> 已清理产物中的多余 logo"
fi
}
4.3.5 配置还原
这是最关键的一步------构建完成后必须还原所有被修改的文件:
bash
restore_config() {
local project="$1"
local project_path="$PROJECT_ROOT/${PROJECT_PATHS[$project]}"
cd "$PROJECT_ROOT"
# ① 用 git checkout 还原配置文件(最安全的方式)
case $project in
databoard)
git checkout -- "${PROJECT_PATHS[databoard]}/static/config.js"
;;
manage|purchase|supplier)
git checkout -- "${PROJECT_PATHS[$project]}/static/config.json"
git checkout -- "${PROJECT_PATHS[$project]}/pages.json"
git checkout -- "${PROJECT_PATHS[$project]}/manifest.json"
;;
esac
# ② 还原 Logo 文件夹
if [ -d "$project_path/.logo_backup" ]; then
mv "$project_path/.logo_backup"/* "$project_path/static/images/" 2>/dev/null || true
rmdir "$project_path/.logo_backup" 2>/dev/null || true
fi
# ③ 还原被排除的 uni_modules
if [ -d "$project_path/.module_backup/sy-new-jessibuca" ]; then
mv "$project_path/.module_backup/sy-new-jessibuca" "$project_path/uni_modules/"
rmdir "$project_path/.module_backup"
fi
echo ">>> 已还原 $project 配置"
}
为什么用
git checkout而不是"改之前先备份、改之后再恢复"?因为
git checkout还原的是 Git 仓库中的版本,绝对准确 。而备份-恢复方案在脚本中途崩溃时可能导致备份文件丢失。git checkout是幂等的,多次执行结果一样。
4.4 Windows / Linux 双平台兼容
脚本需要同时在 Windows(Git Bash)和 Linux 上运行,需要处理两个平台差异:
bash
# 检测操作系统
IS_WINDOWS=0
if [[ "$(uname -s)" == MINGW* ]] || [[ "$(uname -s)" == MSYS* ]] || [[ "$(uname -s)" == CYGWIN* ]]; then
IS_WINDOWS=1
fi
# 自动检测可用的 Python 命令(兼容 Windows 和 Linux)
PYTHON=""
for cmd in python3 python; do
if $cmd -c "print('ok')" 2>/dev/null | grep -q ok; then
PYTHON="$cmd"
break
fi
done
# Git Bash 路径转换:/d/project → D:/project
# Python 不认 Git Bash 的路径格式,必须转换
to_native_path() {
if [ $IS_WINDOWS -eq 1 ]; then
echo "$1" | sed 's|^/\([a-zA-Z]\)/|\1:/|'
else
echo "$1"
fi
}
# 默认输出目录按系统区分
if [ $IS_WINDOWS -eq 1 ]; then
OUTPUT_DIR="${OUTPUT_DIR:-C:/publish}"
else
OUTPUT_DIR="${OUTPUT_DIR:-/home/deploy/release}"
fi
# Windows 下强制 Python 使用 UTF-8(避免中文路径乱码)
export PYTHONUTF8=1
这里有一个隐蔽的坑 :Windows 上 Microsoft Store 安装的 Python 是一个占位符(exit code 49),需要额外查找真实安装路径。上面的
for cmd in python3 python循环 +grep -q ok验证可以可靠地找到可用的 Python。
4.5 微信小程序自动上传
使用微信官方的 miniprogram-ci 实现代码上传:
bash
# 安装
npm install -g miniprogram-ci
# 上传(必须绕过代理)
NO_PROXY="*" no_proxy="*" \
HTTP_PROXY="" HTTPS_PROXY="" http_proxy="" https_proxy="" \
npx miniprogram-ci upload \
--pp dist/build/mp-weixin \
--pkp clients/keys/wx1234567890.key \
--appid wx1234567890abcdef \
--uv "4.0.31" \
-r 1 \
--enable-minify \
--upload-description "v4.0.31 自动发布"
三个关键注意事项:
- 上传密钥 :从微信公众平台后台下载,存放在
clients/keys/目录,必须加入 .gitignore,绝对不能提交到仓库 - 代理问题:微信要求上传请求的源 IP 在白名单中。如果服务器配置了 HTTP 代理,请求会从代理 IP 发出,导致 IP 校验失败。必须清除所有代理环境变量
- 版本号管理 :用
mp-versions.json记录每个 AppID 最近的版本号,脚本自动建议递增下一位
上传成功后,登录微信公众平台 → 管理 → 版本管理,可以看到刚刚通过 CI 自动上传的体验版本:

上图为通过
miniprogram-ci自动上传后,在微信公众平台「版本管理」中看到的开发版本。可以看到版本号、上传时间和备注信息,与脚本中传入的参数完全对应。之后只需在平台上手动点击「提交审核」即可。
4.6 产物输出目录
前端发布产物按项目和平台分目录存放:
release/
├── databoard-web/ ← 数据看板 H5
├── manage-h5/ ← 管理端 H5
├── purchase-h5/ ← 订货端 H5
├── supplier-h5/ ← 供货端 H5
├── manage-mp/ ← 管理端微信小程序
├── purchase-mp/ ← 订货端微信小程序
└── supplier-mp/ ← 供货端微信小程序
H5 产物部署为静态文件,微信小程序产物通过 miniprogram-ci 上传到微信公众平台。
五、AI 辅助的命令式发布交互
5.1 交互层与执行层分离
传统做法是用 Shell 的 read、select 实现交互菜单,但面对 50+ 客户列表 + 多选项目 + 多种发布方式的组合,Shell 交互体验很差。
我们的做法是:用 AI 编码助手(Claude Code)的自定义命令系统做交互层,Shell 脚本做纯执行层。
/publish(AI 命令)
→ 读取 clients.json 展示客户列表
→ 多选项目
→ 选择发布方式
→ 调用 publish-frontend.sh --customer X --projects Y --type Z
脚本完全参数化,不包含任何交互逻辑,方便后续接入 GitLab CI/CD。
5.2 实际使用效果
> /publish web
选择客户:customer_a(客户A)
选择项目:☑ 管理端 ☑ 订货端 ☑ 供货端
选择方式:● 微信小程序
>>> 开始发布:管理端小程序
>>> 已替换配置
>>> 构建成功
>>> 已复制到 release/manage-mp/
>>> 已还原配置
>>> 管理端小程序 微信上传成功 (v4.0.31)
>>> 订货端小程序 微信上传成功 (v4.0.31)
>>> 供货端小程序 微信上传成功 (v4.0.31)
发布完成!请登录微信公众平台提交审核。
一条命令,替代了之前 30 分钟的手动操作。
六、效果对比
| 维度 | 改造前 | 改造后 |
|---|---|---|
| 发布 1 个客户 × 3 小程序 | 手动改配置 → HBuilderX 构建 → 手动上传,~30 分钟 | 一条命令自动完成,~3 分钟 |
| 发布 50 个客户 | 50 × 30 分钟 = 25 小时 | 50 × 3 分钟 = 2.5 小时 |
| 支持平台 | 仅微信小程序 | H5 + 微信小程序 |
| 构建环境 | 仅 Windows(HBuilderX) | Windows + Linux |
| 出错率 | 高(手动改配置易遗漏) | 极低(脚本自动替换和还原) |
| 日常开发 | HBuilderX | 不受影响,继续用 HBuilderX |
七、踩坑总结与最佳实践
7.1 CLI 改造核心经验
UNI_INPUT_DIR=./是 HBuilderX 项目转 CLI 的第一个关键参数@dcloudio包版本必须与 HBuilderX 版本严格对应,否则两端构建结果不一致vue和vue-template-compiler版本必须完全一致(精确到 patch 版本)- 用
sass(dart-sass)替代node-sass,省去 Linux 上的原生编译问题 autoprefixer锁定 9.x,与 uni-app 内部的 PostCSS 版本兼容- 改造时逐个错误解决,不要一次性改太多配置
7.2 自动化发布核心经验
- 配置替换后必须
git checkout还原,这是最容易遗忘也最危险的步骤 - 微信小程序 2MB 限制:多客户 Logo 必须做瘦身
miniprogram-ci上传必须绕过代理,清除 HTTP_PROXY 等环境变量- 密钥文件第一时间加入 .gitignore
- 构建脚本纯参数化,不要有交互逻辑,为 CI/CD 留接口
- 幂等性设计:脚本失败后重新执行不会产生副作用
八、后续规划
- APK 自动打包:通过 DCloud 云打包 API 或本地 Android SDK
- 批量发布:一条命令循环发布所有客户
- GitLab CI/CD 集成:push 到 release 分支后自动触发构建发布
- Docker 化前端构建:将 Node.js 环境容器化,消除环境依赖
- uni-app Vue3 升级:评估迁移到 Vue3 + Vite 的可行性
九、总结
整个项目的技术演进路线:
微信原生小程序 ──→ uni-app (Vue2, HBuilderX 可视化)
│
CLI 改造(本文核心)
│
▼
uni-app (Vue2, CLI 命令行构建)
│
自动化发布脚本 + 多客户配置
│
▼
/publish 一键发布 50+ 客户 × 多平台
核心收获:
- HBuilderX → CLI 的改造本质是把 IDE 内置的隐式配置全部变为显式依赖
- package.json 的每个依赖都有明确的对应关系,版本匹配是最容易出错的地方
- 配置驱动发布是处理多客户定制化的关键设计思路
- 脚本参数化 + 交互层分离为后续 CI/CD 奠定了基础
希望这篇实战记录能帮到正在做 uni-app CLI 改造或小程序自动化发布的同学。