docs: 更新 readme 反映 H5 自动 ZIP 打包和统一配置方式

This commit is contained in:
2026-05-11 11:56:37 +08:00
parent 6033cc2fa0
commit dee2887026
2 changed files with 111 additions and 64 deletions

View File

@@ -83,37 +83,24 @@
📌 **测试版**保留原有 `/hwappx/test/` 📌 **测试版**保留原有 `/hwappx/test/`
💡 **个性化定制示例**:`/hwappx/2811/`,其中 2811 为定制化版客户编号 💡 **个性化定制示例**:`/hwappx/2811/`,其中 2811 为定制化版客户编号
3. 选择菜单栏「发行」 → 「自定义发行」 →(「H5-xcx.aigc-quickapp.com」查看.local.config.js中站点的网址前缀,站点的网址前缀是什么就选自定义发行中相同的格式) 3. 选择菜单栏「发行」 → 「自定义发行」 → 选择对应的站点脚本
例如:网址是5g.aigc-quickapp.com,自定义发行就选H5-5g.aigc-quickapp.com 💡 例如:网址是 `5g.aigc-quickapp.com` 选 `H5-5g.aigc-quickapp.com`
网址是dev.aigc-quickapp.com,自定义发行就选H5-xcx.aigc-quickapp.com
4. 修改以下配置: 4. 修改以下配置:
1. **网站标题**:快应用 1. **网站标题**:快应用
2. **网站域名**:当前客户域名(示例:xcx.aigc-quickapp.com或者5g.aigc-quickapp.com) 2. **网站域名**:当前客户域名
3. 确认后点击「发行」等待构建完成 3. 确认后点击「发行」等待构建完成
⚠️ **注意**:底部控制台弹出“项目 lucky_shop 导出Web成功,路径为:D:\项目文件\项目根目录\unpackage\dist\build\web”后再执行下一步 ⚠️ **注意**:构建完成后自动输出到 `unpackage/dist/build/h5/`,格式为 `{域名}-{base路径}.zip`
💡 **示例**:`项目 lucky_shop 导出Web成功,路径为:D:\0.项目源码\lucky_shop\unpackage\dist\build\web` 💡 **示例**:`xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip`
5. 按控制台提示的路径找到 `web` 目录,将该目录下所有文件手动打包成一个 `.zip` 压缩包
*仅打包文件,不包含外层 web 目录*
##### 4.2.1.2 【重命名】 ##### 4.2.1.2 【交付与最终发布】🔍
1. 按版本类型规范重命名压缩包: 1. 将生成的 `.zip` 文件发送给运维人员
- **通用版**:`hwappx-common-域名-时间.zip`
💡 **示例**:`hwappx-common-xcx.aigc-quickapp.com-2026-01-24.zip`
- **定制化版**:`客户名称-定制化---hwappx-客户编号-域名-时间.zip`
💡 **示例**:`POCT检测分析平台-定制化---hwappx-2811-xcx.aigc-quickapp.com-2026-01-24.zip`
🚫 **禁止**:压缩包命名禁止包含 `/ \ : * ? " < > |` 等特殊字符
##### 4.2.1.3 【交付与最终发布】🔍
1. 将重命名后的压缩包发送给运维人员
2. **运维人员操作流程**: 2. **运维人员操作流程**:
1. 解压压缩包 1. 解压压缩包
2. 打开快应用开发者工具,导入解压后的代码目录 2. 打开快应用开发者工具,导入解压后的代码目录
3. 验证代码无报错后,执行上传发布操作 3. 验证代码无报错后,执行上传发布操作
##### 4.2.1.3 【测试流程】
##### 4.2.1.3 【测试流程】
H5:打开自己的浏览器输入网址(https://5g.aigc-quickapp.com/hwappx/common/?uniacid=75)改站点 H5:打开自己的浏览器输入网址(https://5g.aigc-quickapp.com/hwappx/common/?uniacid=75)改站点
如果是test就把hwappx/common/改为hwappx/test/----------改站点 如果是test就把hwappx/common/改为hwappx/test/----------改站点
@@ -134,22 +121,40 @@ H5:打开自己的浏览器输入网址(https://5g.aigc-quickapp.com/hwappx/co
- **commitId**:当前构建的 Git 短提交 ID - **commitId**:当前构建的 Git 短提交 ID
- **buildTime**:构建时间 - **buildTime**:构建时间
> **开发模式**:HMR 热更新时自动跳过重复注入,不影响开发体验。 > **开发模式**:HMR 热更新时由 webpack emit 钩子自动处理,不影响开发体验。
### 5.2 可用的 npm 脚本命令 ### 5.2 自动 ZIP 打包
| 命令 | 说明 | **H5 自定义发行**构建完成后自动执行:
| 步骤 | 说明 |
|------|------| |------|------|
| `npm run mp-weixin` | 拷贝 site.js + config 文件 + 创建 ZIP 压缩包(用于手动触发) | | 重命名输出目录 | `{域名}-{base路径用.连接}`(如 `xcx.aigc-quickapp.com-hwappx.common`) |
| 创建 ZIP 文件 | 文件名格式:`{目录名}-{Git提交ID}-{上海时区时间戳}.zip` |
| 清理 | ZIP 创建完成后自动删除源目录 |
> 生产构建时,ZIP 打包由构建插件自动触发,通常无需手动执行此命令。 💡 **示例 ZIP**:`xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip`
### 5.3 构建后处理功能(mp-weixin.patch.js) **微信小程序**生产构建时自动执行:
- 拷贝 `site.js` / `project.config.json` / `project.private.config.json` 到构建目录
- 创建 ZIP 包(文件名包含 Git 提交 ID)
- 复制 `project.config.json` 和 `project.private.config.json` 到构建目录 ### 5.3 环境配置
- 复制 `site.js` 到构建目录
- 创建 ZIP 压缩包(文件名包含 Git 提交 ID) 域名配置统一通过 `package.json` 中各脚本的 `env.UNI_APP_DOMAIN` 传入:
- 自动打开 ZIP 文件所在目录
```json
"h5-xcx.aigc-quickapp.com": {
"title": "...",
"env": {
"UNI_PLATFORM": "h5",
"UNI_APP_DOMAIN": "https://xcx.aigc-quickapp.com/"
},
"define": { "PRODUCTION": true }
}
```
`config.js` 和 `vue.config.js` 均通过 `process.env.UNI_APP_DOMAIN` 读取,**新增/删除脚本无需改动代码**。
--- ---

View File

@@ -1,4 +1,5 @@
const path = require('path'); const path = require('path');
const fs = require('fs');
const { execSync } = require('child_process'); const { execSync } = require('child_process');
const { zionUniMpLoadPackagePlugin } = require('zion-uniapp-mp-load-package/webpack'); const { zionUniMpLoadPackagePlugin } = require('zion-uniapp-mp-load-package/webpack');
@@ -25,11 +26,8 @@ try {
// 从 manifest.json 读取 h5.router.base,拼接到输出路径 // 从 manifest.json 读取 h5.router.base,拼接到输出路径
let baseSuffix = ''; let baseSuffix = '';
try { try {
const manifest = require('./manifest.json'); const manifestPath = path.join(__dirname, 'manifest.json');
if (manifest?.h5?.router?.base) { baseSuffix = _readManifestBase(manifestPath);
// 将 /hwappx/common/ 转换为 hwappx.common
baseSuffix = manifest.h5.router.base.replace(/^\/+|\/+$/g, '').replace(/\//g, '.');
}
} catch (e) {} } catch (e) {}
// 从脚本 env 中获取域名(优先使用 UNI_APP_DOMAIN,否则从 UNI_SCRIPT 提取) // 从脚本 env 中获取域名(优先使用 UNI_APP_DOMAIN,否则从 UNI_SCRIPT 提取)
let domain = ''; let domain = '';
@@ -52,6 +50,25 @@ try {
console.log('NODE_ENV 环境变量未设置,默认使用开发模式'); console.log('NODE_ENV 环境变量未设置,默认使用开发模式');
} }
/**
* 读取 manifest.json 中的 h5.router.base,去掉注释后解析
* 返回 base 路径的 . 连接形式(如 /hwappx/common/ → hwappx.common)
*/
function _readManifestBase(manifestPath) {
try {
const raw = fs.readFileSync(manifestPath, 'utf8');
// 去掉 /* */ 和 // 注释(HBuilder 生成的 JSON 允许注释)
const clean = raw.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, '');
const manifest = JSON.parse(clean);
if (manifest?.h5?.router?.base) {
return manifest.h5.router.base.replace(/^\/+|\/+$/g, '').replace(/\//g, '.');
}
} catch (e) {
console.warn('[manifest] 读取 h5.router.base 失败:', e.message);
}
return '';
}
/** /**
* 自定义 webpack 插件:在通用产物(vendor.js)中内联注入版本数据 * 自定义 webpack 插件:在通用产物(vendor.js)中内联注入版本数据
* 利用 emit 钩子在资源写入磁盘前处理,支持 HMR(每次重编译都会自动注入) * 利用 emit 钩子在资源写入磁盘前处理,支持 HMR(每次重编译都会自动注入)
@@ -148,8 +165,8 @@ class VersionInjectPlugin {
} catch (e) { } catch (e) {
console.error('补丁脚本执行失败:', e.message); console.error('补丁脚本执行失败:', e.message);
} }
} else if (platform === 'h5' && UNI_SCRIPT && this._cachedCommitId) { } else if (platform === 'h5' && UNI_SCRIPT) {
this._zipH5Build(UNI_SCRIPT, this._cachedCommitId); this._zipH5Build(compilation);
} }
callback(); callback();
@@ -157,15 +174,9 @@ class VersionInjectPlugin {
} }
} }
_zipH5Build(UNI_SCRIPT, commitId) { _computeH5OutputName() {
// 计算输出目录名(与 UNI_OUTPUT_DIR 逻辑一致) // 计算输出目录名: {域名}-{base用.连接}
let baseSuffix = ''; let baseSuffix = _readManifestBase(path.join(__dirname, 'manifest.json'));
try {
const manifest = require('./manifest.json');
if (manifest?.h5?.router?.base) {
baseSuffix = manifest.h5.router.base.replace(/^\/+|\/+$/g, '').replace(/\//g, '.');
}
} catch (e) {}
let domain = ''; let domain = '';
if (process.env.UNI_APP_DOMAIN) { if (process.env.UNI_APP_DOMAIN) {
try { try {
@@ -174,45 +185,76 @@ class VersionInjectPlugin {
domain = process.env.UNI_APP_DOMAIN.replace(/^https?:\/\//, '').replace(/\/.*$/, ''); domain = process.env.UNI_APP_DOMAIN.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
} }
} else { } else {
domain = UNI_SCRIPT.replace(/^h5-/, ''); domain = (process.env.UNI_SCRIPT || '').replace(/^h5-/, '');
} }
const outputDirName = baseSuffix ? `${domain}-${baseSuffix}` : domain; const result = baseSuffix ? `${domain}-${baseSuffix}` : domain;
console.log('[H5Zip] 输出目录名:', result);
return result;
}
// 上海时区时间(UTC+8) _zipH5Build(compilation) {
const outputDirName = this._computeH5OutputName();
const buildDir = process.env.NODE_ENV === 'production' ? 'build' : 'dev';
const h5Dir = path.join(__dirname, 'unpackage', 'dist', buildDir, 'h5');
// 实际构建输出目录(由 webpack 决定)
const actualOutput = compilation.compiler.outputPath;
const targetDir = path.join(h5Dir, outputDirName);
// 如果路径一致,跳过重命名
if (actualOutput !== targetDir) {
try {
if (fs.existsSync(targetDir)) {
fs.rmSync(targetDir, { recursive: true });
}
fs.renameSync(actualOutput, targetDir);
console.log(`[H5Zip] 输出目录已重命名为: ${outputDirName}`);
} catch (e) {
console.error('[H5Zip] 目录重命名失败:', e.message);
return;
}
} else {
console.log(`[H5Zip] 输出目录名正确: ${outputDirName}`);
}
// 上海时区时间戳格式: 20260511_1058
const now = new Date(); const now = new Date();
const shanghaiOffset = 8 * 60; const shanghaiOffset = 8 * 60;
const shanghaiTime = new Date(now.getTime() + shanghaiOffset * 60 * 1000); const shanghaiTime = new Date(now.getTime() + shanghaiOffset * 60 * 1000);
const ts = shanghaiTime.toISOString().replace(/[^0-9]/g, '').slice(0, 14); const y = shanghaiTime.getFullYear();
const M = String(shanghaiTime.getMonth() + 1).padStart(2, '0');
const d = String(shanghaiTime.getDate()).padStart(2, '0');
const h = String(shanghaiTime.getHours()).padStart(2, '0');
const m = String(shanghaiTime.getMinutes()).padStart(2, '0');
const ts = `${y}${M}${d}_${h}${m}`;
const buildDir = process.env.NODE_ENV === 'production' ? 'build' : 'dev'; const zipFileName = `${outputDirName}-${this._cachedCommitId}-${ts}.zip`;
const sourceDir = path.join(__dirname, 'unpackage', 'dist', buildDir, 'h5', outputDirName); const zipFilePath = path.join(h5Dir, zipFileName);
const destDir = path.join(__dirname, 'unpackage', 'dist', buildDir, 'h5');
const zipFileName = `${outputDirName}-${commitId}-${ts}.zip`;
const zipFilePath = path.join(destDir, zipFileName);
console.log(`\n📦 H5 构建完成,打包 ZIP: ${zipFileName}...`); console.log(`\n📦 H5 ZIP: ${zipFileName}...`);
let command; let command;
const sourcePath = sourceDir.replace(/'/g, "''"); const sourcePath = targetDir.replace(/'/g, "''");
const destPath = zipFilePath.replace(/'/g, "''"); const destPath = zipFilePath.replace(/'/g, "''");
if (process.platform === 'win32') { if (process.platform === 'win32') {
command = `powershell -Command "Compress-Archive -Path '${sourcePath}\\*' -DestinationPath '${destPath}' -Force"`; command = `powershell -Command "Compress-Archive -Path '${sourcePath}\\*' -DestinationPath '${destPath}' -Force"`;
} else { } else {
const sourceBase = path.dirname(sourceDir); const sourceBase = path.dirname(targetDir);
const sourceName = path.basename(sourceDir); const sourceName = path.basename(targetDir);
command = `cd '${sourceBase}' && zip -r '${zipFilePath}' '${sourceName}' -x '*/node_modules/*' '*.git/*' '*.DS_Store'`; command = `cd '${sourceBase}' && zip -r '${zipFilePath}' '${sourceName}' -x '*/node_modules/*' '*.git/*' '*.DS_Store'`;
} }
try { try {
execSync(command, { stdio: 'inherit' }); execSync(command, { stdio: 'inherit' });
console.log(`✅ H5 ZIP 文件已创建: ${zipFileName}`); console.log(`✅ H5 ZIP: ${zipFileName}`);
// 删除已打包的源目录
fs.rmSync(targetDir, { recursive: true, force: true });
console.log(`[H5Zip] 已删除源目录: ${outputDirName}`);
} catch (e) { } catch (e) {
console.error('H5 ZIP 打包失败:', e.message); console.error('H5 ZIP 打包失败:', e.message);
} }
} }
} }
}
}
// 导出配置 // 导出配置
module.exports = { module.exports = {