【uniapp】uniapp热更新WGT资源,简单的多环境WGT打包脚本

前言

在使用 uni-app 构建多环境 App(如 dev/uat/prod)时,常常会面临一个棘手问题:版本号混乱 。由于 manifest.json 是每次打包都需要修改的核心配置文件,一旦在开发分支中修改了版本号,再合并到测试或生产分支时极易出现版本号被覆盖、热更新异常等问题。

本文将介绍如何借助 Node.js 编写的自动化脚本,结合项目内版本映射文件,实现一套适用于多环境的 WGT 自动打包、版本安全管理、可选 Git 提交的高效方案。


一、背景与痛点

在传统打包流程中:

  • 每次打包都需要手动修改 manifest.jsonversionName
  • 多个环境共用一份 manifest,极易发生版本污染
  • 容易遗漏版本号提交,造成热更新失败
  • 没有标准的打包规范,依赖开发者记忆和手动操作

这些问题在多人协作、频繁发布的项目中尤其明显。


二、解决方案设计

我们为此设计了一套脚本驱动的自动打包机制:

  • 使用单独文件 wgt-env-version-map.json 统一管理各环境版本号
  • 打包脚本会临时更新 manifest.json 中的版本号,不影响源码
  • 支持一键构建并生成 .wgt 文件,产物命名规则清晰统一
  • 可选 --commit 参数将版本变更提交至 Git,确保记录完整
  • 最后一步自动还原 manifest.json,防止版本号残留

三、项目结构与配置

1. 环境版本映射文件(src/wgt-env-version-map.json)

json 复制代码
{
  "dev": "1.0.0",
  "uat": "1.1.0",
  "prod": "1.2.0"
}

2. 打包命令(package.json)

json 复制代码
"scripts": {
  "build:wgt": "node script/build-wgt.js",
  "build:wgt-dev": "node script/build-wgt.js --env dev --commit",
  "build:wgt-uat": "node script/build-wgt.js --env uat --commit",
  "build:wgt-prod": "node script/build-wgt.js --env prod --commit",
  ...
}

这些命令将根据环境读取对应版本号,并完成 WGT 包的构建。

3. 构建流程自动化脚本(script/build-wgt.js)

javascript 复制代码
const fs = require('fs')
const { execSync } = require('child_process')
const join = require('path').join
const compressing = require('compressing')

// 从命令行参数获取环境变量
const args = process.argv.slice(2)
let env = 'prod'
let shouldCommit = false

// 解析命令行参数
for (let i = 0; i < args.length; i++) {
  if (args[i] === '--env' && i + 1 < args.length) {
    env = args[i + 1]
    i++
  } else if (args[i] === '--commit') {
    shouldCommit = true
  }
}

// 验证环境参数
if (!['dev', 'uat', 'prod'].includes(env)) {
  console.error('环境参数只能是 dev/uat/prod 之一')
  console.error('用法: node build-wgt.js --env [dev|uat|prod] [--commit]')
  // 在npm run build:wgt 后面添加 -- 即可自定义传入参数
  console.error('或者: npm run build:wgt -- --env [dev|uat|prod] [--commit]')
  process.exit(1)
}

console.log('====================')
console.log(`当前环境: ${env}`)
if (shouldCommit) console.log('将更新后的版本映射文件提交到git')
console.log('====================')

// 读取版本映射文件
let versionMap
try {
  const versionMapContent = fs.readFileSync(__dirname + '/../src/wgt-env-version-map.json', 'utf-8')
  versionMap = JSON.parse(versionMapContent)
  console.log(`读取版本映射文件成功`)
} catch (e) {
  console.error('读取wgt-env-version-map.json文件失败:', e.message)
  process.exit(1)
}

// 根据环境获取版本号
const targetVersion = versionMap[env]
if (!targetVersion) {
  console.error(`未找到环境 ${env} 对应的版本号`)
  process.exit(1)
}

console.log(`目标wgt版本: ${targetVersion}`)

// 读取manifest文件并保存原始内容
const manifestPath = __dirname + '/../src/manifest.json'
let originalManifestContent = fs.readFileSync(manifestPath, 'utf-8')

// 解析manifest内容
let manifestContent = originalManifestContent
  .replace(/\/\/.*$/gm, '') // 移除单行注释
  .replace(/\/\*[\s\S]*?\*\//g, '') // 移除多行注释
  .replace(/,(\s*[}\]])/g, '$1') // 修复尾随逗号问题

let manifest
try {
  manifest = JSON.parse(manifestContent)
  console.log(`当前manifest版本: ${manifest.versionName}`)
} catch (e) {
  console.error('解析manifest.json文件失败:', e.message)
  // 输出处理后的内容前几行,便于调试
  console.log('处理后的JSON内容片段:')
  console.log(manifestContent.slice(0, 300) + '...')
  process.exit(1)
}

// 临时更新manifest中的版本号
manifest.versionName = targetVersion
const updatedManifestContent = JSON.stringify(manifest, null, 2)

// 写入临时更新的manifest
fs.writeFileSync(manifestPath, updatedManifestContent, 'utf-8')
console.log(`临时更新manifest版本为: ${targetVersion}`)

console.log('====================')

// 根据环境变量选择构建命令
let buildCommand = 'npm run build:app'
if (env === 'dev') {
  buildCommand = 'npm run build:app-dev'
} else if (env === 'uat') {
  buildCommand = 'npm run build:app-uat'
} else if (env === 'prod') {
  buildCommand = 'npm run build:app-prod'
}

try {
  // 编译
  console.log(`执行构建命令: ${buildCommand}`)
  console.log(execSync(buildCommand, { encoding: 'utf-8' }))
  console.log('====================')

  console.log('编译完成,将编译后的文件打包为wgt包')

  // 打包
  const tarStream = new compressing.zip.Stream()

  // 使用绝对路径
  const rootDir = join(__dirname, '..')
  const targetPath = join(rootDir, 'dist', 'build', 'app')
  const wgtOutputDir = join(rootDir, 'dist', 'build', 'wgt')
  const targetFile = join(wgtOutputDir, `packageName-${targetVersion}-${env}.wgt`)

  // 确保源目录存在
  if (!fs.existsSync(targetPath)) {
    throw new Error(`目录 ${targetPath} 不存在,请先确保构建命令成功执行`)
  }

  // 确保输出目录存在
  if (!fs.existsSync(wgtOutputDir)) {
    fs.mkdirSync(wgtOutputDir, { recursive: true })
    console.log(`创建目录: ${wgtOutputDir}`)
  }

  let paths = fs.readdirSync(targetPath)
  paths.forEach(function (item) {
    let fPath = join(targetPath, item)
    tarStream.addEntry(fPath)
  })

  tarStream.pipe(fs.createWriteStream(targetFile))

  console.log('====================')
  console.log(`环境: ${env}`)
  console.log(`版本: ${targetVersion}`)
  console.log('构建完成')
  console.log('====================')

  // 如果需要提交到git
  if (shouldCommit) {
    try {
      console.log('将版本映射文件提交到git...')
      execSync('git add src/wgt-env-version-map.json', { encoding: 'utf-8' })
      execSync(`git commit -m "chore: 更新wgt版本 ${env}环境: ${targetVersion}"`, {
        encoding: 'utf-8'
      })
      execSync('git push', { encoding: 'utf-8' })
      console.log('Git提交成功')
    } catch (error) {
      console.error('Git提交失败:', error.message)
    }
  }
} catch (error) {
  console.error('构建过程出错:', error.message)
} finally {
  // 无论构建成功还是失败,都要恢复原始的manifest内容
  console.log('恢复manifest.json原始内容...')
  fs.writeFileSync(manifestPath, originalManifestContent, 'utf-8')
  console.log('manifest.json已恢复到原始状态')
}

核心逻辑如下:

  1. 读取版本映射文件,获取当前环境对应的版本号
  2. 临时修改 manifest.json 中的 versionName
  3. 执行构建命令(如 npm run build:app-uat
  4. 将产物压缩为 .wgt 文件(如:packageName-1.1.0-uat.wgt
  5. 恢复 manifest.json 为原始内容
  6. 可选将 wgt-env-version-map.json 提交到 Git

wgt输出路径:dist/build/wgt/

四、使用方式

打包并自动提交版本

bash 复制代码
npm run build:wgt-uat

等同于执行:

bash 复制代码
node script/build-wgt.js --env uat --commit

自定义参数打包

bash 复制代码
npm run build:wgt -- --env prod --commit

五、为何不用直接修改 manifest.json?

如果你的多个环境在不同分支中手动改动 manifest.json,会导致如下问题: 如果你的多个环境在不同分支中手动改动 manifest.json,会导致如下问题:

  • dev作为开发分支,版本迭代快,dev版本1.0.10,prod版本1.0.1,如果dev一直向上合代码,会导致prod版本快速增长
  • 容易出现提交遗漏、冲突、热更新错误版本等风险

通过版本号集中管理 + 构建脚本动态注入,我们能做到:

  • 一套代码,多套版本号隔离
  • 构建时注入,源码中始终保持一致
  • 版本提交清晰、规范、可回溯

六、后续建议

这个脚本只实现了比较简单的功能,后续可以继续拓展,比如打包后自动上传,接入CI/CD等...

相关推荐
刘大浪1 小时前
uniapp 小程序 学习(一)
学习·小程序·uni-app
小前端大牛马1 小时前
react中hook和高阶组件的选型
前端·javascript·vue.js
刺客-Andy1 小时前
React第六十二节 Router中 createStaticRouter 的使用详解
前端·javascript·react.js
秋田君2 小时前
深入理解JavaScript设计模式之策略模式
javascript·设计模式·策略模式
菜鸡爱上编程4 小时前
React16,17,18,19更新对比
前端·javascript·reactjs·react
我命由我123454 小时前
VSCode - VSCode 转换英文字母的大小写
开发语言·javascript·ide·vscode·编辑器·html·软件工具
陈龙龙的陈龙龙4 小时前
uniapp 金额处理组件
前端·javascript·uni-app
我命由我123454 小时前
VSCode - VSCode 让未被编辑的标签页不被自动关闭
前端·javascript·ide·vscode·编辑器·html·js
layman05285 小时前
openeuler 虚拟机:Nginx 日志分析脚本
前端·javascript·nginx
烛阴5 小时前
快速上手Axios:前端开发者必备技能
前端·javascript