Skip to content

Stage05|Performance ​

Lesson069|启动速度、首屏时间与分阶段加载 ​

Tags: #Creator2.x #Stage05 #Startup #Loading #FirstScreenDifficulty: ⭐⭐⭐⭐☆


真实问题:启动总时长缩短了,玩家为什么觉得更慢 ​

旧版本 4 秒后直接进入可操作大厅。新版本总加载只需 3.5 秒,但前 3 秒一直黑屏,最后 0.5 秒才出现 UI。技术指标变好,感知体验变差。

启动至少有三个指标:

text
First Frame:第一次有画面
First Meaningful Paint:第一次有意义内容
Time To Interactive:第一次可操作
All Ready:后台内容全部完成

优化不能只看 All Ready。

一、本课目标 ​

启动性能关注的是用户从点击到可操作的时间,而不是单个资源加载函数耗时。本课建立启动阶段分解和分阶段加载方案。


二、启动时间线 ​

text
进程启动
→ 引擎初始化
→ 脚本加载
→ 首场景加载
→ 资源解码
→ Node / Component 创建
→ 首次布局和渲染
→ 用户可操作

每一步都可能成为首屏瓶颈。


三、首屏可用和资源完整不是一回事 ​

可以把资源分为:

text
启动必需
首屏展示必需
首屏交互必需
首屏后增强
用户触发时加载

优先让用户看到稳定且可操作的最小界面,再延迟非关键资源。


四、分阶段加载示例 ​

text
阶段 1:启动 Logo 和基础配置
阶段 2:登录界面和网络必需资源
阶段 3:主界面首屏资源
阶段 4:后台预加载下一页面
阶段 5:用户进入时再加载大型模块

每一阶段都要有失败、取消和重试策略。


五、首屏优化的常见手段 ​

  • 减少启动 Scene 的节点和依赖。
  • 拆分大 Prefab 和大图集。
  • 延迟非关键 Label、动画和特效。
  • 预热关键资源,避免首次交互时卡顿。
  • 控制并发解码和纹理上传。
  • 避免在 onLoad 做同步重计算。

六、案例:启动页过度预加载 ​

text
启动页一次 preload 全部商城、活动、战斗、排行榜资源

结果:

  • 首屏等待时间变长。
  • 内存峰值提高。
  • 低端设备可能解码失败或被系统杀死。
  • 用户只打开主界面,却承担了所有模块成本。

应按用户路径和首屏需要拆分资源。


七、启动优化必须测什么 ​

text
进程启动时间
首屏可见时间
首屏可交互时间
首个操作响应时间
首屏内存峰值
首次进入大模块耗时

不要把“加载条走完”当成唯一用户体验指标。


八、常见误区 ​

误区一:启动时把所有资源加载完最安全 ​

会增加首屏等待和内存峰值。

误区二:加载条百分比就是用户等待时间 ​

不同资源阶段的成本不相同,进度应基于真实任务设计。

误区三:只测开发机启动 ​

低端设备、弱网和不同存储速度会改变结果。

误区四:延迟所有资源就一定更快 ​

首屏后首次操作可能出现新的卡顿,需要预热关键资源。


九、练习与答案 ​

  1. 启动性能至少要区分哪些时间点?
  2. 为什么不能启动时 preload 所有模块?
  3. 延迟加载的风险是什么?
  4. 首屏资源应该如何分层?

答案:

  1. 进程启动、首屏可见、首屏可交互和首次操作响应。
  2. 增加等待、峰值和低端设备风险。
  3. 用户首次进入模块时可能出现等待或卡顿,需要预热和兜底。
  4. 按启动、首屏、首屏后、用户触发等优先级分层。

给启动链路打标 ​

text
T0 进程/页面开始
T1 引擎启动
T2 Boot Scene 可见
T3 登录/本地配置就绪
T4 Lobby 基础 UI 可见
T5 输入可用
T6 非关键内容完成

每个平台选择可获取的最早 T0,并保持版本间口径一致。

Creator 2.4.x 实验:关键与非关键资源拆分 ​

StartupProbe.ts ​

ts
const { ccclass, property } = cc._decorator;

@ccclass
export default class StartupProbe extends cc.Component {
    @property(cc.Node)
    basicUi: cc.Node = null;

    private startAt = Date.now();

    onLoad() {
        this.mark('boot-onLoad');
    }

    start() {
        this.basicUi.active = true;
        this.mark('first-meaningful-ui');
        this.loadDeferred();
    }

    private loadDeferred() {
        cc.resources.load(
            'startup/non-critical-panel',
            cc.Prefab,
            (err) => {
                this.mark(err ? 'deferred-failed' : 'deferred-ready');
            }
        );
    }

    private mark(name: string) {
        cc.log(
            '[STARTUP]',
            name,
            Date.now() - this.startAt
        );
    }
}

对照 ​

  1. 启动前加载所有资源再显示 Boot。
  2. 先显示基础 UI,再后台加载非关键。
  3. 模拟弱网和冷缓存。
  4. 记录 T2/T4/T5/T6、内存峰值和失败降级。
  5. 确认后台加载不抢占登录/大厅关键路径。

什么资源属于首屏关键 ​

问:

  • 没它能否显示品牌/进度?
  • 没它能否登录或进入核心功能?
  • 能否用占位图/默认配置?
  • 失败是否阻塞用户?
  • 后台加载是否会抢 CPU/IO/内存?

不按“以后总会用到”判定关键。

真实项目故障:为了零等待预加载全部 Bundle ​

启动阶段并发加载大厅、战斗、商城和活动。高速设备总时长不错,低端机却因并发解码和内存峰值崩溃。

修复阶段:

text
Boot 必需
→ 账号/配置
→ Lobby 可交互
→ 空闲时预取高概率下一步
→ 用户选择后加载具体模块

加上并发限制、优先级、取消和内存预算。

启动回归矩阵 ​

条件必测
冷/热缓存首次安装、二次启动
网络离线、弱网、正常
设备低端、主流、高端
账号新用户、老用户、大存档
版本首次升级、资源缓存迁移
状态后台恢复、来电/切应用

版本边界与练习答案 ​

Creator 2.4.x Web、小游戏和 Native 启动入口/工具不同,标记点要映射到各平台,但产品指标保持一致。

  1. First Frame 与 TTI 有何区别? 前者只要求有画面,TTI 要求核心输入可响应。
  2. 延后加载是否一定提升体验? 不一定,后台任务可能抢资源,或用户很快进入模块又等待。
  3. 为什么测新用户与老用户? 存档、缓存、迁移和内容规模不同。
  4. 启动优化如何防止功能回归? 为每阶段定义依赖和失败回退,做离线/弱网/升级矩阵测试。

十、本课总结 ​

text
启动优化的目标是尽早可见、尽早可操作,同时控制首屏峰值。
加载时机应服务用户路径,而不是把资源一次性全部搬进内存。

十一、下一课预告 ​

text
Lesson070|微信小游戏的包体、内存、渲染与平台限制