version.json 结构设计:客户端更新检查和热更新

version.json 没有统一标准,但根据用途不同有一套通用的最佳实践字段设计。

最简结构

{
  "version": "1.0.0"
}

适合油猴脚本、小工具。只做版本对比时够用。

推荐通用结构

{
  "version": "1.2.3",
  "build": 10203,
  "name": "MyApp",
  "releaseDate": "2026-05-16T10:00:00Z",
  "description": "修复登录问题和性能优化",
  "forceUpdate": false,
  "downloadUrl": "https://cdn.example.com/app-1.2.3.zip",
  "sha256": "a1b2c3d4..."
}
字段作用
version语义化版本号,方便理解
build数字版本,方便代码比较(newBuild > currentBuild
forceUpdate是否强制更新(旧版本无法继续使用)
sha256文件校验,防止下载被篡改
downloadUrl客户端直接取用,不用硬编码

Electron / 桌面应用更新

{
  "version": "2.1.0",
  "minimumVersion": "2.0.0",
  "forceUpdate": true,
  "url": "https://cdn.example.com/app-2.1.0.exe",
  "sha256": "xxxxx",
  "size": 104857600,
  "releaseNotes": [
    "新增插件系统",
    "修复内存泄漏问题"
  ]
}

minimumVersion 表示低于此版本的客户端必须强制升级,配合 forceUpdate: true 使用。size 可用于计算下载进度。

前端静态资源版本控制

{
  "version": "2026.05.16",
  "commit": "8f3ab21",
  "buildTime": 1747372000,
  "assets": {
    "app.js": "a1b2c3",
    "style.css": "d4e5f6"
  }
}

前端通过定期 fetch('/version.json') 检测资源 hash 变化,自动提示用户刷新。commitbuildTime 方便追踪具体是哪次 CI 构建。

油猴脚本自动更新

{
  "version": "1.0.5",
  "meta": "https://example.com/script.meta.js",
  "script": "https://example.com/script.user.js"
}

脚本内:

GM_xmlhttpRequest({
    url: "https://example.com/version.json",
    onload(res) {
        const remote = JSON.parse(res.response);
        if (remote.version > GM_info.script.version) {
            GM_notification("有新版本 " + remote.version);
        }
    }
});

远程配置 + Feature Flags

{
  "version": "1.3.0",
  "env": "production",
  "api": "https://api.example.com",
  "featureFlags": {
    "newUI": true,
    "betaFeature": false
  }
}

这已经接近”远程配置中心”,避免了把 API 地址硬编码进客户端。

版本号规范建议

推荐 语义化版本

变更类型示例
不兼容的 API 变更2.0.0
新增功能(向下兼容)1.3.0
Bug 修复1.3.1

避免用纯数字 "version": 15——后期无法区分是大版本还是补丁。

Python 读写 version.json

import json

# 读取
with open("version.json", "r", encoding="utf-8") as f:
    data = json.load(f)

# 修改后写回(覆盖)
data["version"] = "1.2.4"
with open("version.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

不要用 open("file", "rw") — Python 不支持 "rw" 模式,会报 ValueError: must have exactly one of create/read/write/append mode。读写合一用 "r+"f.seek(0) + f.truncate(),但最简单的方案是分开读和写。