# 版本管理方案 ## 🎯 问题 前端每次更新版本,后端也需要手动同步版本信息,容易出错且效率低。 ## ✅ 解决方案 ### 方案一:自动生成 version.json(已实现)⭐⭐⭐ 前端打包时自动生成 `version.json` 文件,后端可以直接读取。 #### 工作流程 ``` 1. 开发者修改 package.json 中的 version ↓ 2. 运行 pnpm run build ↓ 3. Vite 自动读取 package.json 版本 ↓ 4. 构建完成后生成 dist/version.json ↓ 5. 部署到服务器 ↓ 6. 后端通过 HTTP 请求获取前端版本 ``` #### 生成的 version.json 格式 ```json { "version": "0.0.1", "buildTime": "2025-12-30T10:30:00.000Z", "gitCommit": "abc123", "environment": "production" } ``` #### 后端获取版本的方式 **方式 1:直接读取静态文件(推荐)** 如果前端部署在 Nginx/CDN,后端可以直接访问: ```bash curl https://your-domain.com/version.json ``` 后端 API 实现示例: ```typescript // Node.js / Elysia 示例 app.get("/api/app/version", async (ctx) => { // 从前端静态资源读取版本 const response = await fetch("https://your-frontend-domain.com/version.json"); const frontendVersion = await response.json(); const { platform, currentVersion } = ctx.query; return { version: frontendVersion.version, forceUpdate: compareVersion(currentVersion, frontendVersion.version) < 0, updateMessage: "修复了一些问题", updateUrl: platform === "ios" ? "https://apps.apple.com/app/xxx" : "https://play.google.com/store/apps/details?id=xxx", minSupportVersion: "0.0.1", }; }); ``` **方式 2:部署时同步到后端** 在 CI/CD 中,部署前端时自动同步 version.json 到后端: ```yaml # GitHub Actions / GitLab CI 示例 - name: Deploy Frontend run: | pnpm run build # 上传到前端服务器 rsync -avz dist/ user@frontend-server:/var/www/html/ # 同时将 version.json 复制到后端 scp dist/version.json user@backend-server:/app/frontend-version.json ``` 后端直接读取本地文件: ```typescript import fs from "node:fs"; app.get("/api/app/version", async (ctx) => { const versionFile = fs.readFileSync("/app/frontend-version.json", "utf-8"); const { version } = JSON.parse(versionFile); return { version, forceUpdate: false, // ... }; }); ``` --- ## 📝 使用步骤 ### 1. 修改版本号 只需修改 `package.json` 中的 `version` 字段: ```json { "version": "1.2.3" // 只改这里 } ``` ### 2. 构建项目 ```bash pnpm run build ``` 构建完成后会在 `dist/` 目录生成 `version.json`: ``` dist/ ├── index.html ├── assets/ └── version.json ← 自动生成 ``` ### 3. 部署 将整个 `dist/` 目录部署到服务器,`version.json` 会一起部署。 --- ## 🔧 原生应用版本同步 ### iOS (Xcode) 修改 `ios/App/App/Info.plist`: ```xml CFBundleShortVersionString 1.2.3 ``` ### Android (build.gradle) 修改 `android/app/build.gradle`: ```gradle versionName "1.2.3" // 与 package.json 保持一致 ``` ### 自动化脚本(可选) 创建 `scripts/sync-version.js`: ```javascript #!/usr/bin/env node import { execSync } from "node:child_process"; import fs from "node:fs"; const packageJson = JSON.parse(fs.readFileSync("./package.json", "utf-8")); const version = packageJson.version; console.log(`Syncing version ${version} to native projects...`); // iOS const infoPlist = "./ios/App/App/Info.plist"; let plistContent = fs.readFileSync(infoPlist, "utf-8"); plistContent = plistContent.replace( /CFBundleShortVersionString<\/key>\s*.*?<\/string>/, `CFBundleShortVersionString\n\t${version}` ); fs.writeFileSync(infoPlist, plistContent); // Android (需要安装 gradle 解析库或手动更新) console.log("Please manually update android/app/build.gradle versionName"); console.log("✓ Version synced!"); ``` 在 `package.json` 中添加脚本: ```json { "scripts": { "version:sync": "node scripts/sync-version.js", "build:ios": "npm run version:sync && ionic cap sync ios", "build:android": "npm run version:sync && ionic cap sync android" } } ``` --- ## 🎨 版本号规范 遵循**语义化版本 (Semantic Versioning)**: ``` 主版本号.次版本号.修订号 ↓ ↓ ↓ 1 . 2 . 3 - 主版本号:不兼容的 API 修改 - 次版本号:向下兼容的功能新增 - 修订号:向下兼容的问题修正 ``` ### 版本更新示例 - `0.0.1` → `0.0.2` - 修复 bug - `0.0.2` → `0.1.0` - 新增功能 - `0.1.0` → `1.0.0` - 重大更新 --- ## 🚀 CI/CD 集成 ### GitHub Actions 示例 ```yaml name: Build and Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: "18" - name: Install dependencies run: pnpm install - name: Build run: pnpm run build env: NODE_ENV: production - name: Deploy to server run: | rsync -avz dist/ user@server:/var/www/html/ - name: Notify backend run: | curl -X POST https://your-backend.com/api/webhook/frontend-deployed \ -H "Content-Type: application/json" \ -d '{"version": "'$(node -p "require('./package.json').version")'"}' ``` --- ## 📊 方案对比 | 方案 | 优点 | 缺点 | 推荐度 | |-----|------|------|--------| | **自动生成 version.json** | 完全自动化、前后端解耦 | 需要后端额外请求 | ⭐⭐⭐⭐⭐ | | 手动同步 | 简单直接 | 容易忘记、出错 | ⭐ | | 共享配置文件 | 统一管理 | 需要 Monorepo | ⭐⭐⭐ | | 环境变量 | 灵活 | 部署时需手动设置 | ⭐⭐ | --- ## ✅ 总结 1. **前端**:只需修改 `package.json` 的 `version` 字段 2. **构建**:运行 `pnpm run build` 自动生成 `version.json` 3. **后端**:通过 HTTP 请求或读取文件获取版本信息 4. **原生应用**:可选使用脚本自动同步版本号 **一次配置,永久受益!** 🎉 ## 后端 API 实现示例 - 版本检查接口 ```ts // 方案二:从本地文件读取(适用于前后端部署在同一服务器) import fs from "node:fs"; import { Elysia, t } from "elysia"; // 版本比较工具函数 function compareVersion(version1: string, version2: string): number { const v1Parts = version1.split(".").map(Number); const v2Parts = version2.split(".").map(Number); const maxLength = Math.max(v1Parts.length, v2Parts.length); for (let i = 0; i < maxLength; i++) { const v1Part = v1Parts[i] || 0; const v2Part = v2Parts[i] || 0; if (v1Part < v2Part) return -1; if (v1Part > v2Part) return 1; } return 0; } // 方案一:从前端静态资源读取版本(推荐) async function getFrontendVersionFromURL(): Promise<{ version: string; buildTime: string; gitCommit: string; environment: string; }> { const response = await fetch("https://your-frontend-domain.com/version.json"); return await response.json(); } function getFrontendVersionFromFile(): { version: string; buildTime: string; gitCommit: string; environment: string; } { const content = fs.readFileSync("/app/frontend-version.json", "utf-8"); return JSON.parse(content); } // 应用商店链接配置 const APP_STORE_URLS = { ios: "https://apps.apple.com/app/id123456789", android: "https://play.google.com/store/apps/details?id=riwa.ionic.app", }; // 版本策略配置(可存储在数据库) const VERSION_POLICIES = { minSupportVersion: "0.0.1", // 最低支持版本 forceUpdateVersions: ["0.0.1"], // 需要强制更新的版本列表 updateMessages: { "zh-CN": "修复了一些问题并优化了性能", "en-US": "Bug fixes and performance improvements", }, }; // Elysia 路由定义 const app = new Elysia() .get( "/api/app/version", async ({ query, headers }) => { const { platform, currentVersion } = query; const lang = headers["accept-language"]?.split(",")[0] || "en-US"; try { // 获取前端版本信息 const frontendVersion = await getFrontendVersionFromURL(); // 或使用本地文件: const frontendVersion = getFrontendVersionFromFile() // 判断是否需要更新 const hasUpdate = compareVersion(currentVersion, frontendVersion.version) < 0; // 判断是否强制更新 let forceUpdate = VERSION_POLICIES.forceUpdateVersions.includes(currentVersion); // 检查是否低于最低支持版本 if (compareVersion(currentVersion, VERSION_POLICIES.minSupportVersion) < 0) { forceUpdate = true; } // 获取更新链接 const updateUrl = platform === "ios" ? APP_STORE_URLS.ios : platform === "android" ? APP_STORE_URLS.android : ""; // 获取更新说明(多语言) const updateMessage = VERSION_POLICIES.updateMessages[lang] || VERSION_POLICIES.updateMessages["en-US"]; return { version: frontendVersion.version, buildNumber: Number.parseInt(frontendVersion.version.replace(/\./g, "")), buildTime: frontendVersion.buildTime, gitCommit: frontendVersion.gitCommit, forceUpdate, updateMessage, updateUrl, minSupportVersion: VERSION_POLICIES.minSupportVersion, releaseNotes: [ "修复了已知问题", "优化了应用性能", "改进了用户界面", ], }; } catch (error) { console.error("Failed to get frontend version:", error); // 降级处理:返回当前版本,不强制更新 return { version: currentVersion, forceUpdate: false, updateMessage: "", updateUrl: "", }; } }, { query: t.Object({ platform: t.Union([t.Literal("ios"), t.Literal("android"), t.Literal("web")]), currentVersion: t.String(), }), response: t.Object({ version: t.String(), buildNumber: t.Optional(t.Number()), buildTime: t.Optional(t.String()), gitCommit: t.Optional(t.String()), forceUpdate: t.Boolean(), updateMessage: t.Optional(t.String()), updateUrl: t.Optional(t.String()), minSupportVersion: t.Optional(t.String()), releaseNotes: t.Optional(t.Array(t.String())), }), } ); export default app; /** * 使用示例: * * 1. 启动后端服务 * 2. 前端请求:GET /api/app/version?platform=ios¤tVersion=0.0.1 * 3. 后端响应: * { * "version": "0.0.2", * "buildTime": "2025-12-30T11:42:43.425Z", * "forceUpdate": false, * "updateMessage": "修复了一些问题并优化了性能", * "updateUrl": "https://apps.apple.com/app/id123456789", * "minSupportVersion": "0.0.1" * } */ /** * 数据库存储方案(可选) * * 如果需要更灵活的版本策略管理,可以将配置存储在数据库: * * CREATE TABLE app_versions ( * id SERIAL PRIMARY KEY, * platform VARCHAR(20) NOT NULL, * version VARCHAR(20) NOT NULL, * min_support_version VARCHAR(20), * force_update BOOLEAN DEFAULT FALSE, * update_message_zh TEXT, * update_message_en TEXT, * update_url TEXT, * release_notes JSONB, * created_at TIMESTAMP DEFAULT NOW() * ); * * 然后从数据库查询版本策略: * const policy = await db.query( * 'SELECT * FROM app_versions WHERE platform = $1 ORDER BY created_at DESC LIMIT 1', * [platform] * ) */ ```