实战 | uni-app (Vue2) HBuilderX 项目改造为 CLI 项目,实现多客户多平台命令行自动化发布

本文记录了将 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。每次版本更新,发布流程是这样的:

  1. 打开 HBuilderX,手动修改 config.json(API 地址、邀请码、Logo 路径)
  2. 修改 pages.json(应用标题)
  3. 修改 manifest.json(小程序 AppID)
  4. 点击 HBuilderX 菜单构建
  5. 打开微信开发者工具上传
  6. 改回配置,换下一个客户,重复以上步骤

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.jsonscripts 中定义两个构建命令:

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 h5mp-weixin 指定目标平台,决定编译产物类型
UNI_INPUT_DIR ./ 指定源码目录为项目根目录 (CLI 默认从 src/ 读取,但 HBuilderX 项目源码在根目录,不设置会报 Cannot find module 'src/manifest.json'

构建命令 vue-cli-service uni-build@dcloudio/vue-cli-plugin-uni 注册的自定义命令,它会读取 manifest.jsonpages.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.jsonpages.jsonApp.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 上安装需要 pythonmakeg++ 等编译工具,经常因为缺少依赖或 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 配置文件
  • 解决 :安装 postcssautoprefixer,在 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.vuefooter.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:h5npm 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 上几乎是标配,不需要额外安装。

微信小程序有 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 自动发布"

三个关键注意事项:

  1. 上传密钥 :从微信公众平台后台下载,存放在 clients/keys/ 目录,必须加入 .gitignore,绝对不能提交到仓库
  2. 代理问题:微信要求上传请求的源 IP 在白名单中。如果服务器配置了 HTTP 代理,请求会从代理 IP 发出,导致 IP 校验失败。必须清除所有代理环境变量
  3. 版本号管理 :用 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 的 readselect 实现交互菜单,但面对 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 改造核心经验

  1. UNI_INPUT_DIR=./ 是 HBuilderX 项目转 CLI 的第一个关键参数
  2. @dcloudio 包版本必须与 HBuilderX 版本严格对应,否则两端构建结果不一致
  3. vuevue-template-compiler 版本必须完全一致(精确到 patch 版本)
  4. sass(dart-sass)替代 node-sass,省去 Linux 上的原生编译问题
  5. autoprefixer 锁定 9.x,与 uni-app 内部的 PostCSS 版本兼容
  6. 改造时逐个错误解决,不要一次性改太多配置

7.2 自动化发布核心经验

  1. 配置替换后必须 git checkout 还原,这是最容易遗忘也最危险的步骤
  2. 微信小程序 2MB 限制:多客户 Logo 必须做瘦身
  3. miniprogram-ci 上传必须绕过代理,清除 HTTP_PROXY 等环境变量
  4. 密钥文件第一时间加入 .gitignore
  5. 构建脚本纯参数化,不要有交互逻辑,为 CI/CD 留接口
  6. 幂等性设计:脚本失败后重新执行不会产生副作用

八、后续规划

  1. APK 自动打包:通过 DCloud 云打包 API 或本地 Android SDK
  2. 批量发布:一条命令循环发布所有客户
  3. GitLab CI/CD 集成:push 到 release 分支后自动触发构建发布
  4. Docker 化前端构建:将 Node.js 环境容器化,消除环境依赖
  5. 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 改造或小程序自动化发布的同学。

相关推荐
147API3 小时前
微软 Copilot Cowork 深度解析:用 Kotlin + 147API 手搓一个 AI Agent
kotlin·claude·147api·copilot cowork
147API3 小时前
Claude API 429 限速治理:RPM/ITPM/OTPM + 令牌桶(Kotlin)
java·spring·kotlin·claude
哪 吒3 小时前
GPT-5.4上线,编程能力超过Claude Opus 4.6
gpt·ai·chatgpt·openai·claude·gemini
a187927218313 小时前
【教程】打通本地 IDE AI 与云端 AI 的记忆壁垒:基于 COS 的跨 AI 终端记忆共享与通信系统
人工智能·ai·ai编程·claude·mem·agents·vibe coding
qq_12498707533 小时前
基于springboot的个性化服装搭配推荐小程序(源码+论文+部署+安装)
spring boot·后端·spring·微信小程序·小程序·毕业设计·毕业设计源码
XPoet4 小时前
AI 编程工程化:AI 时代程序员的基本功
aigc·ai编程·claude
多厘7 小时前
Ralph Wiggum 自治循环入门指南, 让 AI 给你写一夜代码
claude
小凡同志7 小时前
Claude Code Skill(技能)完全指南
ai编程·claude
golang学习记7 小时前
Claude Code 官宣新 AI 功能!随时随地 AI 为你打工
人工智能·claude