恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
uni-app 多客户多平台自动发布:HBuilderX 工程改造 CLI 实战
首页
资讯中心
/
uni-app 多客户多平台自动发布:HBuilderX 工程改造 CLI 实战
uni-app 多客户多平台自动发布:HBuilderX 工程改造 CLI 实战
发布时间:2026/9/19 17:29:00
有一段时间我的工作状态基本是打开 HBuilderX同时打开六七个 uni-appVue2项目挨个点“发行”选微信小程序等编译完再切到 H5等到新客户上线那几天一天要手动重复几十次几乎一模一样的操作。后来项目越来越多客户定制需求也越来越多每次发版前还要确认“这个包是给哪家客户的”“manifest 里的 appid 换没换”“接口域名改没改”手一抖就酿成事故。这篇文章就是想把这段折腾经历完整复盘一下如何把 uni-appVue2的 HBuilderX 工程改造成标准 CLI 工程再通过命令行方案把多客户、多平台的自动发布跑起来。改造完之后我的日常就变成了敲一行命令然后等着十几个包依次落地不定制、不串包、不靠人肉记忆。如果你也有类似的痛点这篇应该能帮你省下不少踩坑时间。1. 为什么要从 HBuilderX 项目改造成 CLI 项目1.1 两种工程形态的本质区别uni-app 项目从创建方式上主要分两条路一条是直接用 HBuilderX 新建工程文件放在项目根目录编译和打包能力内置在 IDE 里另一条是基于 vue-cli 创建的标准 CLI 工程工程文件统一放在 src 目录下编译和打包由 npm scripts 驱动。很多团队一开始图省事直接用 HBuilderX 创建工程因为确实方便新建页面、运行到浏览器、一键发行小程序都能在图形界面里完成。但一旦进入“多客户多平台自动发布”这个阶段HBuilderX 的图形化操作反而成了最大的瓶颈。没有人愿意守着 IDE 一遍遍点按钮更不愿意在凌晨发版的时候还要远程桌面控制一台办公电脑去点发行。CLI 工程带来的最大变化是构建过程从“人肉点击”变成了“命令执行”。凡是能在终端里跑的东西就能写进脚本里就能接进 Jenkins、GitHub Actions 这类持续集成系统里。HBuilderX 工程和 CLI 工程的核心差异我用一个表格总结过很多次对比维度HBuilderX 工程CLI 工程工程结构源码在项目根目录源码在 src 目录构建方式HBuilderX 图形界面npm scripts 命令行依赖管理部分插件内置/IDE 导入package.json node_modules自动化能力弱依赖 UI 操作强可脚本化多端构建在 IDE 里选择平台通过 UNI_PLATFORM 变量控制版本管理常规 Git 操作同左但更适合 CI 流程1.2 多客户多平台场景下的三座大山先说最痛的一点人工点击无法批量处理。假设你有 10 个客户每个客户需要微信小程序包、H5 包、App 资源包那就是 30 次“点击 等待 确认”。如果某次构建失败还要从头再来。这根本不是在写程序这是在做体力活。第二痛的是配置串包。每个客户都有自己的微信小程序 appid、Android 报名、接口域名、App 名称、logo 等。HBuilderX 工程的 manifest.json 是写在项目根目录的改一次只对当前工程生效。多个客户往往要复制多份工程或者在发版前手工改配置。只要有一次遗漏就会出现“客户 B 的包里装的是客户 A 的接口地址”这种严重事故。第三痛的是无法接入持续集成。客户临时说“今晚小程序要更新一个紧急修复包”你正好在外面手边没有 IDE这就很被动。但如果构建和上传都能通过命令行完成任何一台装了 Node 的电脑都能发版甚至可以设置定时流水线自动检查分支、自动构建、自动上传。所以结论很简单不是 HBuilderX 不好用而是它在“多客户多平台自动化发布”这个场景下不够用。CLI 工程化不是炫技是被需求逼出来的必要改造。2. 改造方案选型三条路线怎么选2.1 方案一全新创建 CLI 工程再迁移业务代码用vue create -p dcloudio/uni-preset-vue#vue2 new-project或npx degit dcloudio/uni-preset-vue#vue2 new-project新建一个干净的 uni-app CLI 工程然后把原 HBuilderX 工程里的 pages、static、uni_modules、manifest.json、pages.json 等资源考进去。这个方案的好处是工程结构最干净CLI 依赖版本完全是官方推荐的组合不会出现“缺这个依赖”“少那个插件”的诡异问题。坏处是如果你的 HBuilderX 工程已经持续迭代很久迁移时很容易漏文件特别是那些散落在根目录的自定义脚本、模板文件。2.2 方案二在现有 HBuilderX 工程上补充 CLI 能力我推荐不新建工程直接在当前工程根目录补上 package.json、vue.config.js、babel.config.js、postcss.config.js 等 CLI 必需文件然后把源码整体挪进 src 目录。这样做的好处是项目历史、Git 记录都能完整保留迁移风险更多集中在工程结构调整上。实际操作时我一般建议先用方案一生成一个骨架工程拿到官方推荐的 package.json 和配置文件然后再把这些文件复制到老工程根目录最后统一调整目录结构。这相当于把两种方案的优势结合起来新骨架保证依赖正确老工程保证业务不丢。2.3 方案三继续用 HBuilderX 脚本外挂也有团队问我能不能不改造工程结构通过 RPA 或模拟点击 HBuilderX 的按钮来自动发行。我试过一些偏门做法结论是不稳定一升级 IDE 就废而且出了问题非常难排查。HBuilderX 没有提供完善的命令行发行接口与其在半自动状态里将就不如一次性改成 CLI 工程。2.4 我最终采用的目录结构模板改造完成后我习惯把多客户发布相关的脚本统一放在项目根目录的 scripts 和 configs 下整体结构大概长这样project-root ├── package.json ├── vue.config.js ├── babel.config.js ├── postcss.config.js ├── configs/ │ ├── customers/ │ │ ├── customer-a.js │ │ └── customer-b.js │ ├── templates/ │ │ └── project.config.json │ └── release.js ├── scripts/ │ ├── sync-manifest.js │ ├── sync-pages.js │ ├── publish-h5.js │ └── publish-weixin.js └── src/ ├── pages.json ├── manifest.json ├── App.vue ├── main.js ├── uni.scss ├── pages/ ├── static/ └── uni_modules/这套目录的用意在于业务代码全部待在 src 里符合 CLI 工程的规范多客户配置和发布脚本集中在 configs 和 scripts 里不会污染业务代码未来要接 CI入口永远只有一个node configs/release.js。3. 核心迁移步骤实操3.1 生成标准 CLI 工程骨架并统一依赖先找一个临时目录执行下面的命令生成一个基础 CLI 工程npx degit dcloudio/uni-preset-vue#vue2 tmp-uniapp-cli执行完成后进入 tmp-uniapp-cli 目录把 package.json、babel.config.js、postcss.config.js、vue.config.js、.gitignore 这些根级配置文件全部复制到老工程根目录。此时不要急着npm install先把后面几节的结构调整做完避免依赖装完又反复动文件。需要注意uni-app Vue2 的 CLI 工程依赖集中在dcloudio/开头的包上package.json 里会写死一批版本组合。因为 HBuilderX 内置编译器和 CLI 的编译器本质同源版本偏差太大会导致条件编译行为不一致所以要么直接沿用骨架生成的版本要么在升级 HBuilderX 之后同步调整依赖版本。我踩过的坑是CLI 依赖比 HBuilderX 编译器新很多结果某些页面在 HBuilderX 里运行正常用 CLI 构建后样式错乱。3.2 源码目录从根目录收敛到 srcHBuilderX 工程默认是这样分布的old-mall ├── pages.json ├── manifest.json ├── App.vue ├── main.js ├── uni.scss ├── pages/ ├── static/ └── uni_modules/CLI 工程要求所有这些都放进 src 目录old-mall ├── package.json ├── vue.config.js ├── configs/ ├── scripts/ └── src/ ├── pages.json ├── manifest.json ├── App.vue ├── main.js ├── uni.scss ├── pages/ ├── static/ └── uni_modules/迁移时我用的是“新建 src 目录 逐项移动”的方式没有用一条git mv直接挪原因是有些历史遗留文件根本不该进 src。比如老工程根目录下的 .hbuilderx 配置、unpackage 编译缓存这些都要排除掉。另外如果你的 App 端有自定义原生插件或资源通常放在 src/app-plus 之类的目录下也要一起迁移。移动完之后最容易被忽略的是各种相对路径引用。HBuilderX 工程的静态资源经常写成/static/xxx.png这在 CLI 工程里依然可用但你自己写的一些相对路径比如../static/logo.png在结构变化后可能会失效需要全局搜索排查。3.3 检查 pages.json 和 manifest.json 的可迁移性pages.json 基本不用改它是 uni-app 跨端页面路由的统一配置HBuilderX 和 CLI 的解析标准一致。唯一需要注意的是如果 pages.json 里有注释严格来说它不是标准 JSON但 uni-app 编译器允许这种写法迁移后也不要手贱去“格式化”它容易把注释清掉。manifest.json 是另一个重点。CLI 工程的 manifest.json 放在 src/manifest.json 下HBuilderX 打开 CLI 工程也是识别这个位置的。迁移后要确认里面这几项没有丢应用的 name、appidDCloud 应用标识各平台的 appid 配置比如 mp-weixin.appid、h5 的域名配置App 模块配置比如推送、地图、支付等用到的原生模块各平台的图标和启动图路径这里有个很容易踩的坑manifest.json 里 App 模块配置选没选直接影响后续 App 云打包或者离线打包能不能用对应功能。如果你原来的 HBuilderX 工程是在图形界面上勾选模块的迁移后要打开 src/manifest.json 确认模块配置字段还在。3.4 配置 vue.config.js 和 npm scriptsuni-app CLI 工程本质是一个 vue-cli 工程所以 vue.config.js 里可以做很多定制。我常用的最小配置是这样的const path require(path) module.exports { transpileDependencies: [dcloudio/uni-app], configureWebpack: { resolve: { alias: { : path.resolve(__dirname, src) } } }, devServer: { port: 8081 } }这里有两个细节。第一uni-app CLI 默认已经帮你配置了 别名指向 src但显式写出来更稳妥尤其是你后续要引入一些自定义目录时。第二devServer.port 就是修改本地 H5 调试端口的地方常见于热词里那个“hbuilderx 启动修改端口”的需求——CLI 工程里改端口不需要进 IDE 设置改这个文件就够了。package.json 里的 scripts 也要重新整理。uni-app CLI 的构建指令组合方式是环境变量 命令{ scripts: { dev:h5: cross-env NODE_ENVdevelopment UNI_PLATFORMh5 vue-cli-service uni-serve, dev:mp-weixin: cross-env NODE_ENVdevelopment UNI_PLATFORMmp-weixin vue-cli-service uni-serve, build:h5: cross-env NODE_ENVproduction UNI_PLATFORMh5 vue-cli-service uni-build, build:mp-weixin: cross-env NODE_ENVproduction UNI_PLATFORMmp-weixin vue-cli-service uni-build, build:app-plus: cross-env NODE_ENVproduction UNI_PLATFORMapp-plus vue-cli-service uni-build } }cross-env 的作用是解决 Windows 和 macOS/Linux 上设置环境变量的语法差异。如果不装它Windows 下没办法用NODE_ENVproduction UNI_PLATFORMh5这种写法。我见过不少同事不装 cross-env结果脚本只能在 mac 上跑Windows 上直接报错。配置完这些就可以先跑一次npm run dev:h5验证本地开发环境再跑一次npm run build:mp-weixin验证构建链路。这两个验证通过迁移最核心的部分就完成了。4. 多客户多平台资源隔离与动态配置4.1 客户配置文件的目录设计多客户自动发布要解决的第一件事就是把每个客户的差异点集中管理起来。我最终用的是 configs/customers 目录下每个客户一个文件的方式// configs/customers/customer-a.js module.exports { name: customer-a, appName: 客户A商城, description: 客户A的商城小程序/H5, platforms: { h5: { title: 客户A商城, domain: https://h5.customer-a.com, baseURL: https://api.customer-a.com }, mp-weixin: { appid: wx1234567890, setting: { es6: true, minify: true } }, app: { dcloudAppid: __UNI__XXXXXXX, androidPackage: com.customer.a.app, iosBundleId: com.customer.a.app, appName: 客户A商城, versionName: 1.0.0, versionCode: 100 } } }集中管理的收益是肉眼可见的新增客户不用再复制整个项目只新增一个配置文件然后在发布命令里指定客户名就行。客户配置和代码仓库走同一个版本管理每次谁的配置变了Git 记录里一目了然。4.2 用 Node 脚本动态生成 manifest 差异配置配置归配置最终要让 uni-app 编译器读到的 manifest.json 是“当前客户”的完整配置。我写了一个scripts/sync-manifest.js核心逻辑就是读模板、读客户配置、合并、写回 src/manifest.json。const fs require(fs) const path require(path) const customerName process.env.CUSTOMER || default const customerConfig require(path.resolve(__dirname, ../configs/customers/${customerName}.js)) const manifestPath path.resolve(__dirname, ../src/manifest.json) const manifest JSON.parse(fs.readFileSync(manifestPath, utf-8)) // 合并多平台配置 if (customerConfig.platforms[mp-weixin]) { manifest[mp-weixin] { ...manifest[mp-weixin], ...customerConfig.platforms[mp-weixin], appid: customerConfig.platforms[mp-weixin].appid } } if (customerConfig.platforms.h5) { manifest.h5 { ...manifest.h5, ...customerConfig.platforms.h5 } } if (customerConfig.platforms.app) { manifest[app-plus] { ...manifest[app-plus], ...customerConfig.platforms.app } } fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)) console.log([sync-manifest] manifest 已更新为 ${customerName} 的配置)这段代码看起来很朴素但干了一件特别关键的事把“改配置”这个高风险手工操作变成了“可重复执行的脚本”。每次执行发布前都会清掉上一个客户留下的痕迹再写入当前客户的配置从机制上杜绝串包。4.3 接口域名和业务配置的差异化注入manifest 解决的是平台注册信息但业务代码里用到的接口域名、统计 ID、分享文案这些也需要跟着客户走。我总结了几种常见做法按推荐程度排个序。第一条件编译。uni-app 原生支持#ifdef H5、#ifdef MP-WEIXIN这类注释但条件编译只区分平台区分不了客户。所以客户维度不能只靠条件编译。第二在 common/config.js 里写一份默认配置构建前用脚本覆盖。比如先生成 src/common/env.js把接口域名、静态资源地址写进去业务代码统一 import 这个文件。第三也是我更推荐的统一通过一个运行时配置入口读取。在小程序端可以读取manifest.json的某个自定义字段在 H5 端可以读取window.__INITIAL_CONFIG__。但这样实现成本高如果你的客户数量级在几十个以内脚本覆盖 env.js 是最省事的。我实际采用的是“构建前生成 env.js”的方案。脚本会根据客户配置里的 baseURL 生成如下文件// src/common/env.generated.js module.exports { BASE_URL: https://api.customer-a.com, H5_DOMAIN: https://h5.customer-a.com }所有业务代码统一引用这份生成文件接口请求封装层也从这里读 BASE_URL。这样客户切换前后业务代码不用动一行。5. 命令行自动化发布落地细节5.1 一条命令打通“配置同步 编译构建”当客户配置和同步脚本都准备好后我把发布入口收敛到了一个 Node 脚本configs/release.js。它的核心流程是三段式同步配置、按平台构建、产物上传。// configs/release.js const { execSync } require(child_process) const customerName process.env.CUSTOMER || default const platform process.env.PLATFORM || h5 const version process.env.VERSION || 1.0.0 function run(cmd) { console.log([exec] ${cmd}) execSync(cmd, { stdio: inherit, cwd: path.resolve(__dirname, ..) }) } // 1. 同步客户配置 run(node scripts/sync-manifest.js) run(node scripts/sync-env.js) // 2. 按平台构建 const buildCmd cross-env NODE_ENVproduction UNI_PLATFORM${platform} vue-cli-service uni-build run(buildCmd) // 3. 按平台上传承建产物 if (platform mp-weixin) { run(node scripts/publish-weixin.js --version${version} --descauto-release) } else if (platform h5) { run(node scripts/publish-h5.js --version${version}) } console.log([release] ${customerName} ${platform} ${version} 发布完成)实际使用中我一般会在项目根目录加一个简单的启动脚本或者在 package.json 里加几个组合命令{ scripts: { release:h5: cross-env CUSTOMERdefault PLATFORMh5 VERSION1.0.0 node configs/release.js, release:weixin: cross-env CUSTOMERdefault PLATFORMmp-weixin VERSION1.0.0 node configs/release.js, release:app: cross-env CUSTOMERdefault PLATFORMapp-plus VERSION1.0.0 node configs/release.js } }这么做之后日常发版就从“打开 IDE 手动点”变成了npm run release:weixin需要给指定客户发指定平台时CUSTOMERcustomer-a PLATFORMmp-weixin VERSION2.3.0 npm run release:weixin这段逻辑是整个自动化方案的心脏。后面的持续集成、定时构建本质都是换一种方式调用这几条命令。5.2 微信小程序自动化上传miniprogram-ciCLI 工程执行uni-build只会生成dist/build/mp-weixin的构建产物真正上传到微信平台还需要官方工具链。最常用的方案是微信官方提供的 miniprogram-ci 包。先安装npm install miniprogram-ci --save-dev然后写一个最小化的上传脚本// scripts/publish-weixin.js const path require(path) const ci require(miniprogram-ci) const appid require(path.resolve(__dirname, ../src/manifest.json))[mp-weixin].appid const project new ci.Project({ appid, type: miniProgram, projectPath: path.resolve(__dirname, ../dist/build/mp-weixin), privateKeyPath: path.resolve(__dirname, ../keys/weixin-private.key), ignores: [node_modules] }) ci.upload({ project, version: process.argv.find((_, i) process.argv[i - 1] --version) || 1.0.0, desc: process.argv.find((_, i) process.argv[i - 1] --desc) || auto release, setting: { es6: true, minify: true } }).then(() { console.log(微信小程序上传成功) }).catch(err { console.error(微信小程序上传失败, err) process.exit(1) })这里有两个容易掉坑的点。第一私钥文件的获取路径是微信公众平台 → 开发管理 → 开发设置 → 小程序代码上传密钥生成后需要下载到本地。这个密钥要像密码一样管好建议不进 Git 仓库而是放到 CI 系统的机密变量里发版前由流水线写入本地。第二dist/build/mp-weixin 目录下需要有 project.config.json。CLI 构建不一定自动生成这个文件所以我在 configs/templates 里放了一个模板同步配置的脚本会每次把模板复制过去。如果缺这个文件miniprogram-ci 会直接报错。5.3 H5 自动化发布产物同步到服务器或 CDNH5 端的 CLI 构建产物在 dist/build/h5。自动化发布最简单的形态是先把产物打包成 tar.gz然后通过 scp 或者对象存储工具上传到指定服务器。我在 scripts/publish-h5.js 里做了一个很直接的事情用 zip 打包产物然后调用上传脚本到服务器。如果你有 OSS 或者云存储也可以用官方 CLI 工具。tar -czf dist/h5-${VERSION}.tar.gz -C dist/build/h5 . scp dist/h5-${VERSION}.tar.gz deployyour-company-server:/data/releases/ ssh deployyour-company-server cd /data/www/h5 tar -xzf /data/releases/h5-${VERSION}.tar.gz ln -sfn /data/www/h5 /data/www/h5.current生产环境不建议直接覆盖文件用软链接切换版本更安全发版失败还能快速回滚。这个思路和 App 发布里的灰度策略是一样的。5.4 App 端的自动化边界App 端和 H5、小程序不一样。CLI 构建只能生成dist/build/app-plus的前端资源真正的原生安装包还需要经过离线打包或云打包。如果团队走离线打包流程是把dist/build/app-plus的资源放入原生工程Android Studio / Xcode再用 gradle 或 xcodebuild 命令行构建安装包。热词里提到的“uni-app 开发的 app 加固后如何重新签名”就属于这个环节的常见需求。Android 端重签名的命令行核心大概长这样# 加固完成后用 apksigner 重新签名 apksigner sign --ks your-release.keystore \ --ks-key-alias your-alias \ --ks-pass pass:your-password \ --out app-signed.apk app-encrypted.apk如果你的团队没有原生开发人力通常只能走 HBuilderX 云打包。云打包在命令行层面的支持有限我目前的处理方式是App 端构建到 app-plus 资源包然后交给 HBuilderX 自定义基座做最后的云打包这一步保留半人工。标题里说“多平台命令行自动化发布”实际上全自动的是 H5 和微信小程序这两个最高频的平台App 端走的是“自动构建资源 半自动云打包”的折中方案这个边界要提前和团队对齐避免需求理解不一致。5.5 多客户循环批量发布如果客户数量多还可以在 release.js 之上再包一层批量任务。比如要给所有启用中的客户发微信小程序可以建立一个 customers/index.js 索引// configs/customers/index.js module.exports { customer-a: require(./customer-a), customer-b: require(./customer-b) }然后写一个遍历器const customers require(./customers) for (const [name, config] of Object.entries(customers)) { if (!config.enabled) continue execSync(cross-env CUSTOMER${name} PLATFORMmp-weixin VERSION${version} node configs/release.js, { stdio: inherit }) }注意每个客户的构建产物都会经过“清空 dist → 同步配置 → 构建 → 上传”的完整流程所以单个客户的配置串包问题不会出现。如果遇到某个客户构建失败我会在脚本里捕获异常记录失败清单继续构建剩余客户最后统一汇总。这样就不会因为一个客户的配置问题阻塞其他所有客户的发版。6. 迁移过程中的高频踩坑记录6.1 package.json 依赖版本不一致导致构建行为变化uni-app Vue2 的 CLI 工程对版本非常敏感。我遇到的比较典型的问题是使用骨架生成的 package.json 里dcloudio/uni-app版本和同事本机 HBuilderX 内置编译器版本不一致结果同一个页面在 HBuilderX 里运行完全正常用 CLI 构建后部分组件不渲染。处理思路尽量固定 CLI 端dcloudio/系列依赖版本不要随意升级如果你主要用 CLI 构建就统一以 CLI 为准别一边用 HBuilderX 调试、一边用 CLI 发版两边版本长期不一致会引出很多诡异问题。6.2 uni_modules 插件迁移后找不到资源HBuilderX 工程里使用 uni_modules 插件很多是直接在 IDE 里通过插件市场安装的。迁移到 CLI 工程后插件目录要跟着源码走放在 src/uni_modules 下。如果发现插件在 CLI 构建时找不到优先检查目录位置对不对其次看插件是否依赖 npm 包有些插件还需要单独npm install。我迁移一个商城项目时就是因为一个支付插件漏了 npm 依赖CLI 构建能过但真机调用时提示找不到模块。最后是去插件源码里看 import 语句把 dependencies 补全才解决。6.3 微信开发者工具打不开或 project.config.json 缺失CLI 构建生成的 dist/build/mp-weixin 默认不带 project.config.json微信开发者工具直接导入会识别不了。我的解决办法是在 configs/templates 里维护一份项目模板内容大致如下{ description: auto generated by uni-app cli, packOptions: { ignore: [] }, setting: { urlCheck: false, es6: true, postcss: true, minified: true, newFeature: true }, compileType: miniprogram, libVersion: 3.7.0, appid: touristappid, projectname: uni-app-cli, condition: {} }然后在每次构建后用脚本把这份模板复制到 dist/build/mp-weixin/project.config.json再把当前客户的 appid 写入。这样微信开发者工具能直接打开miniprogram-ci 也能正常上传。6.4 npm install 失败或构建时 network 异常CLI 工程因为依赖多首次安装确实比 HBuilderX 慢很多。遇到network: unavailable这类提示时我一般按下面几步排查检查当前 npm registry 是不是可访问的镜像源用npm config get registry查看切换到官方源或公司内网源。删除 node_modules 和 package-lock.json重新安装一遍避免中断安装留下的残缺状态。确认没有使用必须走特殊通道才能访问的依赖源尽量保证依赖源在普通办公网络下可稳定访问。我见过不少项目卡在这里其实不是代码问题是依赖安装不完整。CLI 工程一定要把 npm install 这一步在 CI 流水线里单独拆出来并且加上缓存策略否则每次构建都从头装依赖时间成本非常可观。6.5 本地调试端口冲突与热更新失效CLI 工程改了代码后 H5 页面没有热更新大概率是 devServer 配置或端口被占用。vue.config.js 里配置的 port 不能和机器上其他服务冲突。如果是远程开发机可能还需要配置 host: 0.0.0.0。我遇到过因为同时开着两个 uni-app CLI 项目默认端口都是 8080后启动的项目直接报端口占用。把端口显式改成 8081、8082 这类区分开就好了。6.6 HBuilderX 打开 CLI 工程时的注意事项如果你仍然需要在 HBuilderX 里做自定义基座或查看某些 IDE 专属能力注意 HBuilderX 导入 CLI 工程时选择的是项目根目录不是 src 目录。识别成功的标志是 HBuilderX 能读到你 src/manifest.json 里的应用信息。如果打开后提示不是有效的 uni-app 项目检查一下目录结构是不是符合 CLI 工程规范。我自己实际工作中的体会是HBuilderX 和 CLI 并不互斥很多团队会保留 HBuilderX 作为日常开发和真机调试工具把 CLI 作为持续集成和自动发版工具。两者共存的关键就是统一依赖版本、统一 src 目录结构、统一配置生成流程。这套流程跑顺之后我在多客户项目上的收益非常直接新客户接入从原来的“复制项目 手工改配置 改接口域名”变成了“新增一个客户配置文件 跑一条命令”。团队里没有任何人需要记住“给哪个客户发版时该改哪几个地方”因为所有可能被改错的地方都已经在脚本里显式声明、自动执行了。最后再分享一个小技巧每次发布完成后把当次构建的客户名、平台、版本号、构建时间、产物 hash 追加到一个发布记录文件里比如 release-history.json。等哪天客户说“这个线上包里到底是不是我这个版本的代码”你翻一下记录就能定位不用再去猜。