html5游戏存档需兼顾安全、兼容与扩展性:用带crc32校验的safesave/safeload防json解析失败;超5mb存档须切片存入indexeddb;强制版本号控制并显式拒绝不兼容旧档;迁移逻辑需独立测试。

HTML5 游戏存档不能只靠 localStorage 存 JSON 字符串——加密、版本迁移、跨设备同步、大存档崩溃等问题会立刻暴露。
用 localStorage 存基础进度,但必须加校验和
直接 JSON.stringify() 后塞进 localStorage.setItem('save', data) 极易因格式错误或截断导致下次读取时 JSON.parse() 报 SyntaxError: Unexpected token。必须包裹异常处理并写入校验字段:
function safeSave(key, obj) {
try {
const str = JSON.stringify(obj);
const checksum = crc32(str); // 可用轻量函数如 crc32(非 crypto.subtle,兼容性好)
localStorage.setItem(key, JSON.stringify({ data: str, checksum }));
} catch (e) {
console.warn('Save failed:', e);
}
}
function safeLoad(key) {
try {
const item = localStorage.getItem(key);
if (!item) return null;
const { data, checksum } = JSON.parse(item);
if (crc32(data) !== checksum) return null; // 校验失败即丢弃
return JSON.parse(data);
} catch (e) {
return null;
}
}
- 不依赖
try/catch就调用JSON.parse()是绝大多数“存档突然消失”的根源 -
crc32比sha256快得多,且足够防意外损坏;不要用时间戳或随机数代替校验 - 避免在
save中存 DOM 引用、函数、undefined或循环引用对象——序列化前务必JSON.stringify()可过
超过 5MB 的存档必须切片 + indexedDB
Chrome/Firefox 对 localStorage 的硬限制是 5MB(实际触发警告常在 4.5MB 左右),存高清地图状态、录像帧或玩家自定义关卡时极易超限。此时必须切换到 indexedDB,且不能整包读写:
- 按逻辑单元拆分:比如
player_state、world_chunks、inventory_items各自独立 objectStore - 写入用
transaction(..., 'readwrite')+put(),避免用add()导致重复键报错 - 读取单个字段用
get(),别getAll()全拉——大存档下内存暴涨、主线程卡死 - 旧版引擎若没封装 indexedDB,优先用
idb这类轻量 Promise 包装库(仅 1.5KB),别手写 onsuccess/onerror 回调链
存档版本号必须嵌在数据结构里,且拒绝自动降级
游戏更新后旧存档结构变化(比如把 hp 改成 health),若不显式处理,safeLoad() 会静默返回 null 或读出错乱值。版本控制不是可选项:
立即学习“前端免费学习笔记(深入)”;
const SAVE_VERSION = 3;
function saveGame(state) {
safeSave('game_save', {
version: SAVE_VERSION,
data: state
});
}
function loadGame() {
const saved = safeLoad('game_save');
if (!saved || saved.version !== SAVE_VERSION) {
// 显式拒绝:不自动转换,不静默 fallback,提示用户“存档不兼容”
showIncompatibleDialog();
return null;
}
return saved.data;
}
- 版本号必须是数字,别用字符串如
"v1.2"——比对和迁移逻辑会变复杂 - 迁移逻辑(如 v2 → v3)应单独封装为纯函数,测试覆盖所有字段映射,而非在
loadGame()里写 if-else 堆砌 - 上线新版本前,用真实旧存档样本跑一遍迁移函数,确认无
undefined字段漏赋值
真正难的不是存或取,而是当玩家在三台设备上反复覆盖同一个存档、中间还穿插了热更新和本地调试修改时,如何让 version、checksum 和 indexedDB 的事务边界始终咬合。这些细节不提前压测,上线后第一波差评就来自“我的存档没了”。











