refactor: 统一版本信息注入方式
- feat(vue.config): 新增 VersionInjectPlugin,在 webpack emit 钩子中内联注入 __app_version 到 vendor.js / index.html - refactor(scripts): mp-weixin.patch.js 精简为仅拷贝 site.js + ZIP 打包 - feat(index): 首页连击5次显示构建版本信息 - fix(util): 修复 openWxWorkServiceChat fail 回调语法错误
This commit is contained in:
@@ -62,5 +62,5 @@ const localDevConfig = ({
|
||||
uniacid: 2075,
|
||||
domain: 'https://xcx.aigc-quickapp.com/',
|
||||
},
|
||||
})['47']; // 选择要使用的环境配置
|
||||
})['2811']; // 选择要使用的环境配置
|
||||
export default localDevConfig;
|
||||
@@ -111,49 +111,19 @@ export default {
|
||||
}, 2000);
|
||||
},
|
||||
_showVersionInfo() {
|
||||
// #ifdef MP-WEIXIN
|
||||
let v = null;
|
||||
try {
|
||||
const fs = uni.getFileSystemManager();
|
||||
fs.readFile({
|
||||
filePath: 'version.json',
|
||||
encoding: 'utf-8',
|
||||
success: (res) => {
|
||||
try {
|
||||
const v = JSON.parse(res.data);
|
||||
v = typeof __app_version !== 'undefined' ? __app_version : null;
|
||||
} catch (e) {}
|
||||
if (v && v.commitId) {
|
||||
uni.showModal({
|
||||
title: '构建版本信息',
|
||||
content: `提交 ID: ${v.commitId}\n构建时间: ${v.buildTime}`,
|
||||
showCancel: false
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
console.log('未获取到构建版本信息');
|
||||
}
|
||||
} catch (e) {}
|
||||
uni.showModal({ title: '构建版本信息', content: '版本信息不可用', showCancel: false });
|
||||
},
|
||||
fail: () => this._showVersionFromGlobal()
|
||||
});
|
||||
} catch (e) {
|
||||
this._showVersionFromGlobal();
|
||||
}
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
this._showVersionFromGlobal();
|
||||
// #endif
|
||||
},
|
||||
_showVersionFromGlobal() {
|
||||
try {
|
||||
const v = typeof __app_version !== 'undefined' ? __app_version : null;
|
||||
if (v && v.commitId) {
|
||||
uni.showModal({
|
||||
title: '构建版本信息',
|
||||
content: `提交 ID: ${v.commitId}\n构建时间: ${v.buildTime}`,
|
||||
showCancel: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
uni.showModal({ title: '构建版本信息', content: '版本信息不可用', showCancel: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,64 +2,38 @@
|
||||
/**
|
||||
* fix-wechat-miniapp.js
|
||||
*
|
||||
* 该脚本用于修复微信小程序的一些问题:
|
||||
* 1. 将项目根目录下的 site.js 文件拷贝到 unpackage/dist/build/mp-weixin 目录下,如果文件存在则覆盖
|
||||
* 2. 将 project.config.json 及 project.private.config.json 拷贝到目标目录
|
||||
* 3. 生成 version.js(CommonJS 模块)和 version.json 到目标目录
|
||||
* 4. 将 `const site = require("../site");` 和 `const __app_version = require("../version");`
|
||||
* 追加到 unpackage/dist/build/mp-weixin/common/vendor.js 文件内容开头部分,
|
||||
* 如果文件开头已有则跳过
|
||||
* 该脚本用于微信小程序的构建后处理(仅在生产构建时由 vue.config.js 触发):
|
||||
* 1. 将 project.config.json 及 project.private.config.json 拷贝到构建产物目录
|
||||
* 2. 将 site.js 拷贝到构建产物目录
|
||||
* 3. 创建 ZIP 压缩包
|
||||
*
|
||||
* 注意:version.js 生成和 vendor.js 注入已由 webpack 插件 (VersionInjectPlugin) 在 emit 钩子中自动处理。
|
||||
*
|
||||
* 使用:
|
||||
* node fix-wechat-miniapp.js # 打补丁并创建 ZIP 文件(默认 mode=production)
|
||||
* node fix-wechat-miniapp.js --no-zip # 只打补丁,不创建 ZIP 文件
|
||||
* node fix-wechat-miniapp.js --mode development # 使用 development 模式打补丁
|
||||
* node fix-wechat-miniapp.js --mode production # 使用 production 模式打补丁(默认)
|
||||
*
|
||||
* 注意:
|
||||
* - 在 Windows 上路径使用反斜杠也是可以的;脚本使用 path.join 来兼容不同平台。
|
||||
* - 运行前请确认在项目根目录运行(site.js 在当前工作目录下)。
|
||||
* node fix-wechat-miniapp.js # 拷贝文件并创建 ZIP(默认 mode=production)
|
||||
* node fix-wechat-miniapp.js --no-zip # 只拷贝文件,不创建 ZIP
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const fsp = fs.promises;
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const { openFileDirectory } = require('./open-directory');
|
||||
const { createZipWithSystemCommand } = require('./create-zip');
|
||||
|
||||
async function commonPatch(mode = 'production') {
|
||||
async function copyAssets(mode = 'production') {
|
||||
try {
|
||||
|
||||
// 根据当前脚本所在目录(scripts),定位到项目根目录
|
||||
const cwd = path.join(__dirname, '..');
|
||||
|
||||
const srcSitePath = path.join(cwd, 'site.js');
|
||||
const destDir = path.join(cwd, 'unpackage', 'dist', mode === 'production' ? 'build' : 'dev', 'mp-weixin');
|
||||
const destSitePath = path.join(destDir, 'site.js');
|
||||
const vendorPath = path.join(destDir, 'common', 'vendor.js');
|
||||
|
||||
// 1) 检查源文件是否存在
|
||||
const srcExists = await exists(srcSitePath);
|
||||
if (!srcExists) {
|
||||
console.error(`源文件不存在: ${srcSitePath}`);
|
||||
process.exitCode = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保目标目录存在
|
||||
await ensureDir(destDir);
|
||||
|
||||
// 复制 project.config.json 及 project.private.config.json 文件到 destDir 下面
|
||||
// 复制 project.config.json 和 project.private.config.json
|
||||
const configFiles = ['project.config.json', 'project.private.config.json'];
|
||||
for (const fileName of configFiles) {
|
||||
const srcPath = path.join(cwd, fileName);
|
||||
const destPath = path.join(destDir, fileName);
|
||||
|
||||
// 检查源文件是否存在
|
||||
const fileExists = await exists(srcPath);
|
||||
if (fileExists) {
|
||||
if (await exists(srcPath)) {
|
||||
await fsp.copyFile(srcPath, destPath);
|
||||
console.log(`已拷贝: ${srcPath} -> ${destPath}`);
|
||||
} else {
|
||||
@@ -67,73 +41,15 @@ async function commonPatch(mode = 'production') {
|
||||
}
|
||||
}
|
||||
|
||||
// 复制 site.js 到目标目录(覆盖)
|
||||
await fsp.copyFile(srcSitePath, destSitePath);
|
||||
console.log(`已拷贝: ${srcSitePath} -> ${destSitePath}`);
|
||||
|
||||
// 3) 生成 version.js(包含 Git 提交 ID 和构建时间,CommonJS 格式供 require 使用)
|
||||
let commitId = 'unknown';
|
||||
try {
|
||||
commitId = execSync('git rev-parse --short HEAD', { cwd }).toString().trim();
|
||||
} catch (e) {
|
||||
console.warn('无法获取 Git 提交 ID,使用 unknown 代替');
|
||||
// 复制 site.js
|
||||
const srcSitePath = path.join(cwd, 'site.js');
|
||||
if (await exists(srcSitePath)) {
|
||||
await fsp.copyFile(srcSitePath, path.join(destDir, 'site.js'));
|
||||
console.log(`已拷贝: ${srcSitePath} -> ${destDir}`);
|
||||
} else {
|
||||
console.error(`源文件不存在: ${srcSitePath}`);
|
||||
process.exitCode = 2;
|
||||
}
|
||||
const versionData = {
|
||||
commitId,
|
||||
buildTime: new Date().toISOString()
|
||||
};
|
||||
// 同时生成 version.js(供 require)和 version.json(供文件系统读取)
|
||||
const versionJsPath = path.join(destDir, 'version.js');
|
||||
await fsp.writeFile(versionJsPath, `module.exports = ${JSON.stringify(versionData, null, 2)};\n`);
|
||||
console.log(`已生成: ${versionJsPath} (commitId: ${commitId})`);
|
||||
|
||||
const versionJsonPath = path.join(destDir, 'version.json');
|
||||
await fsp.writeFile(versionJsonPath, JSON.stringify(versionData, null, 2));
|
||||
console.log(`已生成: ${versionJsonPath} (commitId: ${commitId})`);
|
||||
|
||||
// 4) 处理 vendor.js:在开头追加 require 语句(如果还没有的话)
|
||||
const vendorExists = await exists(vendorPath);
|
||||
if (!vendorExists) {
|
||||
console.warn(`⚠ 目标 vendor.js 不存在,跳过追加 require。路径: ${vendorPath}`);
|
||||
console.warn(' → 请先执行 HBuilder 构建(uniapp 构建),产生 vendor.js 后再运行此脚本。');
|
||||
console.warn(' → version.json/version.js 已生成,可在首页连击 5 次标题查看版本信息。');
|
||||
return;
|
||||
}
|
||||
|
||||
let vendorContent = await fsp.readFile(vendorPath, 'utf8');
|
||||
|
||||
const requireLines = [
|
||||
'const site = require("../site");',
|
||||
'const __app_version = require("../version");'
|
||||
];
|
||||
|
||||
// 清理已有的重复注入(兼容新旧格式:../version.json 和 ../version)
|
||||
let cleaned = false;
|
||||
const injectPatterns = [
|
||||
/^\s*const\s+site\s*=\s*require\s*\(['"]\.\.\/site(\.js)?['"]\);?\s*\r?\n/gm,
|
||||
/^\s*const\s+__app_version\s*=\s*require\s*\(['"]\.\.\/version(\.json)?['"]\);?\s*\r?\n/gm,
|
||||
];
|
||||
for (const pattern of injectPatterns) {
|
||||
if (pattern.test(vendorContent)) {
|
||||
vendorContent = vendorContent.replace(pattern, '');
|
||||
cleaned = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 把 require 语句 + newline 插入文件最前面。
|
||||
// 保留原始换行风格(若可能)
|
||||
const eol = vendorContent.includes('\r\n') ? '\r\n' : '\n';
|
||||
let prefix = '';
|
||||
|
||||
// 保留可能的 BOM
|
||||
if (vendorContent.charCodeAt(0) === 0xfeff) {
|
||||
prefix = '\uFEFF';
|
||||
vendorContent = vendorContent.slice(1);
|
||||
}
|
||||
|
||||
const newContent = prefix + requireLines.join(eol) + eol + vendorContent;
|
||||
await fsp.writeFile(vendorPath, newContent, 'utf8');
|
||||
console.log(cleaned ? `已清理旧注入并重新注入: ${requireLines.join(', ')}` : `已在 vendor.js 开头追加: ${requireLines.join(', ')}`);
|
||||
} catch (err) {
|
||||
console.error('发生错误:', err);
|
||||
process.exitCode = 1;
|
||||
@@ -141,33 +57,27 @@ async function commonPatch(mode = 'production') {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// 解析命令行参数
|
||||
const argv = process.argv.slice(2);
|
||||
const options = {
|
||||
noZip: argv.includes('--no-zip'),
|
||||
mode: 'production' // 默认值
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
// 解析 --mode 参数
|
||||
const modeIndex = argv.indexOf('--mode');
|
||||
if (modeIndex !== -1 && modeIndex + 1 < argv.length) {
|
||||
options.mode = argv[modeIndex + 1];
|
||||
}
|
||||
|
||||
// 1) 打补丁
|
||||
await commonPatch(options.mode);
|
||||
// await commonPatch('development');
|
||||
// 1) 拷贝文件
|
||||
await copyAssets(options.mode);
|
||||
|
||||
// 2) 创建 ZIP 文件(如果未指定 --no-zip)
|
||||
if (!options.noZip) {
|
||||
// 2) 创建 ZIP(仅 production 模式,且未指定 --no-zip)
|
||||
if (!options.noZip && options.mode === 'production') {
|
||||
const cwd = path.join(__dirname, '..');
|
||||
const buildDir = options.mode === 'production' ? 'build' : 'dev';
|
||||
const sourceDir = path.join(cwd, 'unpackage', 'dist', buildDir, 'mp-weixin');
|
||||
const destDir = path.join(cwd, 'unpackage', 'dist', buildDir);
|
||||
const sourceDir = path.join(cwd, 'unpackage', 'dist', 'build', 'mp-weixin');
|
||||
const destDir = path.join(cwd, 'unpackage', 'dist', 'build');
|
||||
const zipFilePath = await createZipWithSystemCommand(sourceDir, destDir);
|
||||
console.log(`ZIP 文件路径: ${zipFilePath}`);
|
||||
|
||||
// 3) 自动打开zip所在的目录
|
||||
await openFileDirectory(zipFilePath);
|
||||
} else {
|
||||
console.log('跳过创建 ZIP 文件和打开目录');
|
||||
@@ -187,7 +97,6 @@ async function ensureDir(dirPath) {
|
||||
try {
|
||||
await fsp.mkdir(dirPath, { recursive: true });
|
||||
} catch (e) {
|
||||
// ignore if exists
|
||||
if (e.code !== 'EEXIST') throw e;
|
||||
}
|
||||
}
|
||||
|
||||
107
vue.config.js
107
vue.config.js
@@ -1,6 +1,9 @@
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
const { zionUniMpLoadPackagePlugin } = require('zion-uniapp-mp-load-package/webpack');
|
||||
|
||||
console.log('[vue.config] 已加载');
|
||||
|
||||
// 打印出process.env
|
||||
// for (let key in process.env) {
|
||||
// console.log(key, process.env[key]);
|
||||
@@ -29,6 +32,107 @@ try {
|
||||
console.log('NODE_ENV 环境变量未设置,默认使用开发模式');
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 webpack 插件:在通用产物(vendor.js)中内联注入版本数据
|
||||
* 利用 emit 钩子在资源写入磁盘前处理,支持 HMR(每次重编译都会自动注入)
|
||||
* 适用于所有平台(mp-weixin / h5 等)
|
||||
*/
|
||||
class VersionInjectPlugin {
|
||||
constructor() {
|
||||
this._cachedCommitId = null;
|
||||
}
|
||||
apply(compiler) {
|
||||
compiler.hooks.emit.tapAsync('VersionInjectPlugin', (compilation, callback) => {
|
||||
const mode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
|
||||
const platform = process.env.UNI_PLATFORM || '';
|
||||
|
||||
// 获取 Git 提交 ID(缓存,避免每次 HMR 都执行)
|
||||
if (!this._cachedCommitId) {
|
||||
try {
|
||||
this._cachedCommitId = execSync('git rev-parse --short HEAD', { cwd: __dirname }).toString().trim();
|
||||
} catch (e) {
|
||||
this._cachedCommitId = 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
const versionData = { commitId: this._cachedCommitId, buildTime: new Date().toISOString() };
|
||||
const inlineVersion = JSON.stringify(versionData);
|
||||
|
||||
// 尝试注入到 vendor.js(mp-weixin / mp-* 等平台)
|
||||
const vendorKey = 'common/vendor.js';
|
||||
const vendorAsset = compilation.assets[vendorKey];
|
||||
if (vendorAsset) {
|
||||
let content = typeof vendorAsset.source === 'function' ? vendorAsset.source() : vendorAsset._value;
|
||||
if (typeof content !== 'string') content = content.toString();
|
||||
|
||||
// 清理旧注入
|
||||
content = content.replace(/^\s*const\s+site\s*=\s*require\s*\(['"]\.\.\/site(\.js)?['"]\);?\s*\r?\n/gm, '');
|
||||
content = content.replace(/^\s*const\s+__app_version\s*=\s*(require\s*\(['"]\.\.\/version(\.json|\.js)?['"]\)|.+);?\s*\r?\n/gm, '');
|
||||
|
||||
// 只注入版本信息(通用),site.js require 仅在 mp-weixin 生产构建时注入
|
||||
const lines = [`const __app_version = ${inlineVersion};`];
|
||||
if (platform === 'mp-weixin' && mode === 'production') {
|
||||
lines.unshift('const site = require("../site.js");');
|
||||
}
|
||||
|
||||
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
||||
let prefix = '';
|
||||
if (content.charCodeAt(0) === 0xfeff) {
|
||||
prefix = '\uFEFF';
|
||||
content = content.slice(1);
|
||||
}
|
||||
|
||||
content = prefix + lines.join(eol) + eol + content;
|
||||
|
||||
compilation.assets[vendorKey] = {
|
||||
source: () => content,
|
||||
size: () => content.length
|
||||
};
|
||||
|
||||
console.log(`[VersionInject] ✅ vendor.js 已注入 (platform=${platform}, mode=${mode}, commit=${this._cachedCommitId})`);
|
||||
} else {
|
||||
// H5 等平台可能没有 common/vendor.js,改为尝试注入到 index.html
|
||||
// #ifdef H5
|
||||
const htmlKey = 'index.html';
|
||||
const htmlAsset = compilation.assets[htmlKey];
|
||||
if (htmlAsset) {
|
||||
let html = typeof htmlAsset.source === 'function' ? htmlAsset.source() : htmlAsset._value;
|
||||
if (typeof html !== 'string') html = html.toString();
|
||||
// 在 </head> 前注入 script 标签
|
||||
const scriptTag = `<script>var __app_version = ${inlineVersion};</script>`;
|
||||
if (!html.includes('__app_version')) {
|
||||
html = html.replace('</head>', `${scriptTag}\n</head>`);
|
||||
compilation.assets[htmlKey] = {
|
||||
source: () => html,
|
||||
size: () => html.length
|
||||
};
|
||||
console.log(`[VersionInject] ✅ index.html 已注入 (platform=${platform}, commit=${this._cachedCommitId})`);
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
|
||||
// 生产模式下(仅 mp-weixin),额外执行外部脚本完成 site.js 拷贝 + ZIP 打包
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
compiler.hooks.afterEmit.tapAsync('MpWeixinZipPack', (compilation, callback) => {
|
||||
const platform = process.env.UNI_PLATFORM || '';
|
||||
if (platform !== 'mp-weixin') return callback();
|
||||
|
||||
console.log('\n🚀 mp-weixin 生产构建完成,运行补丁脚本(site.js 拷贝 + ZIP 打包)...');
|
||||
try {
|
||||
execSync(`"${process.execPath}" scripts/mp-weixin.patch.js --mode production`, { cwd: __dirname, stdio: 'inherit' });
|
||||
} catch (e) {
|
||||
console.error('补丁脚本执行失败:', e.message);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出配置
|
||||
module.exports = {
|
||||
productionSourceMap: false,
|
||||
@@ -118,5 +222,8 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 所有模式下添加版本注入插件(适用于所有平台)
|
||||
config.plugins.push(new VersionInjectPlugin());
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user