9.5 KiB
小工具性能预算与降级规范
本文提供移动端 WebView 的设计预算与降级要求。Skill 只能检查静态产物和代码设计,不能从源码推断真实帧率、内存或真机表现;没有运行数据时必须明确标记“未实测”。
目录
- §1 交付门禁
- §2 静态数据与长列表
- §3 图片、音频与视频
- §4 WebGL 资源与降级基线
- §5 运行时降级与兜底
- §6 交付检查
1. 交付门禁
| 项目 | 门禁 | 原因 |
|---|---|---|
| 最终 zip | 不超过 10 MiB;建议不超过 2 MiB | 10 MiB 是上传上限,不是性能目标 |
单个 .html / .css / .js / .json |
不设硬上限;超过 2 MiB 触发风险提示 | 大源码会增加读取、解析、编译和峰值内存;即使压缩后很小也一样 |
| 上述文本文件解压后合计 | 不设硬上限;超过 5 MiB 触发风险提示 | 用于发现把数据库或生成产物塞进代码包的情况 |
| 单条 Base64 | 解码后不超过 1 MiB;超过 100 KiB 提示风险,超过 1 MiB 视为大 Base64,必须改成独立包内文件 | Base64 体积约增加 1/3,还会产生字符串与解码副本 |
先检查可用运行时,有哪个就用哪个;两个脚本均为单文件且只使用各自标准库,不要为了审计临时安装 Node、Python 或第三方依赖:
# 有 Node 18+:脚本零第三方依赖
node <skill目录>/scripts/audit_artifact.mjs ./dist
node <skill目录>/scripts/audit_artifact.mjs ./tool.zip
# 有 Python 3:脚本只用标准库,并可检查 zip 内部文本
python3 <skill目录>/scripts/audit_artifact.py ./dist
python3 <skill目录>/scripts/audit_artifact.py ./tool.zip
两个运行时都可用时任选其一;Python 版可额外读取 zip 的文件清单与解压后体积。Node 版对产物目录做完整体积审计,对最终 zip 只检查包体,因此必须在压缩前先审计目录。脚本只依据文件系统和 zip 元数据做确定性检查:zip 超过 10 MiB 报 ERROR;zip 超过建议的 2 MiB、单个或合计文本较大报 WARN。
脚本不解析或猜测 JS / HTML / CSS 语义。Base64、WebGL 结构与降级策略由本规范约束,并在 §6 按代码逻辑审查。仓库发布检查会把两个脚本复制到无项目依赖的临时目录实际执行;依赖 pip / npm 包或相对模块时发布失败。
如果 Node 与 Python 都不可用,仍须人工完成 §6 清单,并至少:查看最终 zip 文件大小;确认 Base64 资源的生成来源与实际字节数;检查最大的 HTML / CSS / JS / JSON 是否混入大型数据;逐项检查 WebGL 预算与降级。交付说明中标记“未运行自动审计”及人工检查结果,不得直接声称审计通过。不要通过改后缀或运行时解压字符串来隐藏大数据,这只会把开销推迟到运行时。
2. 静态数据与长列表
.js / .json 是静态资源,不是数据库。不要把数万条记录、完整业务库、日志或抓取结果生成成 JS 数组。 JSON 与 JS 分文件只能改善组织,不能消除下载、解析和内存成本。
优先按以下顺序缩减:
- 只保留完成核心功能必需的字段和记录,删除重复字段、长描述和历史快照。
- 能预计算的统计、搜索索引和分类结果在构建期生成;不要在首屏对全量数据反复遍历。
- 大型只读数据集改为摘要、分段样例或让用户通过
<input type="file">按需导入。若完整离线数据不可删且仍超门禁,应明确说明该需求不适合小工具,而不是继续打包。 - 用户产生或导入的数据写入 IndexedDB;IndexedDB 用于运行期持久化,不用于掩盖巨大的内置种子数据。
渲染列表时:
- 首屏只创建可见项;长列表使用分页或虚拟滚动,不一次性拼接整份
innerHTML。 - 搜索输入按交互频率和数据规模采用防抖,避免每次按键都触发完整查询;最终输入应及时执行。数据量较大时可预先建立小型索引,避免反复扫描所有字段。
- 非首屏工作分批执行并主动让出主线程,避免把解析、计算和渲染集中在同一个同步流程中。
- 不在循环中反复读写布局属性;批量生成 DOM 后一次挂载。
3. 图片、音频与视频
- 小型图片、图标等可以使用 Base64;单条解码后超过 100 KiB 时应优先改为包内文件,超过 1 MiB 不允许内嵌。
- 图片按真机展示尺寸缩放并压缩,优先 WebP;不要为了 300 px 展示区域打包 4K 原图。
- 音视频先裁剪时长,再降低分辨率、帧率和码率。5 分钟视频通常不适合随小工具离线交付;优先改为短片段、封面 + 交互说明,或让用户运行时自行选择本地视频。
- 视频设置
preload="metadata"或preload="none",提供poster;未进入播放页前不要创建或解码媒体。 - 同时只保留必要的媒体实例;离开页面后暂停播放、清空不再使用的
src并释放对象 URL。
FileReader.readAsDataURL() 可用于用户刚选择的小文件预览或 JSBridge 明确要求 data URI 的短暂转换;大文件不要把结果持久写回静态源码,优先用对象 URL 预览,并在不用时 URL.revokeObjectURL()。注意:即使体积很小,<video> / <audio> 的 data: 媒体源仍不受容器 CSP 支持,应引用包内媒体文件。
4. WebGL 资源与降级基线
WebGL 是可用能力,不代表所有设备都能稳定运行复杂场景。以下数值是生成代码时采用的保守设计预算,不是 Skill 对实际性能的测量结果。目标体验按 30 FPS 可交互设计,60 FPS 仅作为有运行数据支持时的高档增强。
普通 Canvas 2D 不纳入本节的 GPU 分档要求;按实际展示尺寸创建画布、避免无意义的重复全量绘制即可。只有使用 WebGL 上下文时才执行以下资源预算、降档和 context 兜底。
初始预算
| 指标 | 默认档 | 低档 / 降级档 |
|---|---|---|
| WebGL drawing buffer DPR | min(devicePixelRatio, 1.5) |
1 |
| WebGL drawing buffer 像素数 | 不超过约 200 万 | 不超过约 100 万 |
| 单张纹理边长 | 不超过 2048 | 不超过 1024 |
| 估算纹理显存 | 不超过 64 MiB | 不超过 32 MiB |
| 每帧 draw call | 不超过 100 | 不超过 50 |
| 每帧三角形 | 不超过 100k | 不超过 50k |
| 帧率目标 | 稳定 30 FPS,设备有余量再升档 | 稳定 24–30 FPS |
这些是移动 WebView 的保守初始预算。代码可以根据用户提供的设备数据调整;没有实测数据时保持预算与降级路径,不得声称已经达到某个帧率或通过真机性能验收。
渲染规则
- 优先兼容 WebGL 1;使用 WebGL 2 特性时必须检测能力并提供 WebGL 1 或非 WebGL 兜底。
- 初始化先使用低 / 中档,不按高 DPR 直接创建最大缓冲区。尺寸变化时重新计算并继续受像素预算约束。
- 纹理使用实际需要的尺寸;复用纹理、材质、几何体和 framebuffer。估算 RGBA8 纹理最低占用:
宽 × 高 × 4,mipmap 还会额外增加约 1/3。 - 合并可合并的几何与 draw call,视锥 / 距离裁剪不可见对象;粒子、阴影、后处理、透明叠加和实时反射必须能逐项关闭。
- shader 在初始化或切换场景时编译;纹理上传和模型解析分批进行,不在动画帧中首次集中完成。
- 动画循环内避免创建对象 / 数组 / 大字符串;禁止每帧
readPixels()、toDataURL()、大面积getImageData()或同步回读 GPU。 - 页面不可见时通过
visibilitychange停止requestAnimationFrame、媒体和定时器;恢复后重置时间差,避免补算大量帧。 - 处理
webglcontextlost/webglcontextrestored;context 丢失时停止渲染并展示轻量状态,不要无限重建。
5. 运行时降级与兜底
不要依赖机型名单。以实际帧耗时和能力检测决定档位:
- 首次进入采用低 / 中档,逐步加载非必要效果。
- 运行时观测到持续掉帧或交互响应变差时,逐级降低 DPR、粒子数、阴影、后处理、可视距离和动画频率。没有运行数据时只确认降级路径存在,不判断是否达到触发条件。
- 降到最低档仍不可交互时,停止高成本循环并切换 Canvas 2D、静态图或简化 DOM 视图。
getContext()失败、shader 编译 / 链接失败或 context 反复丢失时,必须进入可理解的兜底界面;不能白屏、死循环重试或持续弹错。
如果核心功能并不依赖 3D,默认选择 DOM / CSS / Canvas 2D。WebGL 应解决明确的视觉或计算需求,而不是作为普通表单、列表和信息展示的默认技术栈。
6. 交付检查
- 已按环境选择 Node / Python 自动审计;若两者均不可用,已记录人工审计结果与“未运行自动审计”
- 未把大数据集、日志或生成内容写进 JS / JSON,长列表已分页或虚拟化
- 单条 Base64 解码后不超过 1 MiB;超过 100 KiB 的条目已检查并优先改为独立文件
- 静态设计上未把大文件解析、全量 DOM 构建和大资源初始化集中在首屏同步流程;实际耗时没有运行数据时标记“未实测”
- WebGL 默认受 DPR、像素、纹理、draw call 和三角形预算约束
- WebGL 有动态降档、页面隐藏暂停、context lost 处理和非 WebGL 兜底
- 已区分静态检查与运行实测:有用户提供的运行数据时记录设备、首屏时间、帧率和降级情况;没有数据时标记“性能未实测”,不宣称真机性能合格