用 html-standalone 工具可将 HTML5 小游戏打包为单个 .html 文件,自动内联资源并支持模块处理;需注意路径规范、音频大小限制及 Safari 的 data URL 长度约束。

导出 HTML5 小游戏为单文件:用 html-standalone 工具最稳
直接打包成一个 .html 文件,是离线分发最省事的方式。主流方案里,html-standalone(npm 包)比手动拼接更可靠,它会自动内联 css、js、图片(base64)、音频(小体积时 base64,大音频建议保留外链或用 Blob URL),还能处理 import 和 async 模块。
实操建议:
- 确保所有资源路径是相对路径(如
./assets/sound.mp3),不能是绝对路径或协议开头(https://) - 音频若超过 2MB,
html-standalone默认跳过 base64,此时需在配置中显式设maxInlineSize: 10 * 1024 * 1024(10MB)——但注意 Safari 对 data URL 长度有限制,实际建议 ≤4MB - 用
npx html-standalone index.html --out game.html命令生成;生成后务必用浏览器“打开文件”方式测试(file://协议),别只靠本地服务器 - Canvas 游戏若用了
WebGL纹理压缩格式(如ETC1),单文件无法解决兼容性问题,得降级用PNG或JPEG
Service Worker 缓存策略:离线核心,但别缓存动态请求
单文件解决“首次运行”,Service Worker 才管“后续离线可用”。关键不是“全量缓存”,而是精准控制哪些资源必须离线可用、哪些必须网络优先。
常见错误现象:Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'Window': Invalid name —— 这通常是因为 SW 脚本里写了非法的 cacheName(含空格或特殊字符),或缓存了跨域但没配 CORS 的资源。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 缓存清单用
self.__WB_MANIFEST(Workbox 注入)或手写precacheList = [{url: 'game.js', revision: 'abc123'}],避免 runtime 缓存静态资源时污染版本 - 音频/字体等大资源走
staleWhileRevalidate,HTML/JS/CSS 主体走cacheFirst;但别缓存/api/或带?查询参数的 URL - 注册 SW 前加判断:
if ('serviceWorker' in navigator && location.protocol === 'https:') { ... };HTTP 环境下file://协议不支持 SW,别硬上 - 调试时勾选 Chrome DevTools → Application → Service Workers → “Update on reload”,否则改了代码也不生效
localStorage + IndexedDB 存档离线化:别只存 JSON 字符串
用户进度、关卡记录、道具数量这些,必须在离线时可读写。只用 localStorage.setItem('save', JSON.stringify(data)) 是典型陷阱——超出 5MB 会报 QuotaExceededError,且阻塞主线程。
使用场景:RPG 类游戏存多角色数据、解谜游戏存数百个关卡状态、多人协作小游戏暂存未同步操作。
实操建议:
- 结构化数据优先用
IndexedDB(封装库推荐idb),单次存取 >100KB 时性能差距明显;localStorage只用于存轻量元数据(如最后登录时间戳、音效开关) - 存档前做键名哈希,避免
localStorage键冲突(比如save_v1.2.0_user123);用JSON.stringify(data, null, 0)去掉空格,节省约 15% 体积 - IndexedDB 中音频缓存 blob 数据时,用
URL.createObjectURL(blob)创建临时地址,别存原始 blob 到 store —— 否则每次打开都触发解析 - 在
window.beforeunload里主动await saveToDB(),别依赖定时器或“退出时提示保存”这种不可靠交互
移动端 PWA 安装与离线兜底:manifest.json 不只是图标
Android 上点“添加到主屏幕”后,PWA 能像原生 App 一样启动,但很多开发者只配了 icons 和 name,漏掉关键字段导致离线白屏或跳转失败。
性能影响:缺少 start_url 或其指向的 HTML 未被 precache,会导致“添加后第一次启动失败”;display: "standalone" 缺失则仍显示浏览器地址栏,破坏沉浸感。
实操建议:
-
manifest.json必须包含:start_url: "./index.html"(相对路径)、scope: "./"(保证路由匹配)、display: "standalone"、theme_color(影响状态栏颜色) -
start_url对应的 HTML 文件必须被 Service Worker precache,否则安装后首次离线启动直接net::ERR_FAILED - iOS Safari 不支持
beforeinstallprompt事件,但支持“分享→添加到主屏幕”,所以manifest仍要配全;iOS 上离线运行依赖cacheFirst+ 正确start_url,无额外 API - 检测是否 PWA 环境运行:
if (window.matchMedia('(display-mode: standalone)').matches || navigator.standalone === true),据此关闭“刷新页面”按钮等冗余 UI
真正麻烦的是音频和网络状态切换的协同:SW 缓存了 mp3,但用户离线后调 new Audio(url) 仍可能因 MIME 类型或 CORS 失败;而 localStorage 存档在 iOS 私密模式下直接不可写。这些边界情况,光靠打包工具解决不了。











