低端设备上让 AI 原生应用”感觉快”的实战指南

一句话结论:在 150 美元、3GB 内存、弱信号的手机上,让应用”感觉快”靠的不是某个单一优化——而是把首次绘制拆成流水线、让虚拟列表根据设备内存动态适配、以及把图片加载做成优先级调度系统。这三件事做到位,白屏、卡顿、图片乱跳全消失。


场景还原

做一个新闻资讯应用。首页是卡片滚动列表——标题、摘要、缩略图、日期。纸面上看着简单。

扔到一台 150 美元的 Android 手机上试试:3GB 内存,不稳定的 3G。白屏数秒。图片延迟加载挤得文字到处乱跳。滚动卡成幻灯片。应用”坏了”。

这不是假设——这是每个面向真实用户的工程师最终都要面对的场景。


第一条:首次绘制是流水线,不是单步事件

最大的思维误区:认为”首页加载=请求数据→渲染→完事”。

在低端设备上,这个模式必然导致痛苦的白屏。实际上,首次绘制应该是一串渐进的里程碑:

1
骨架屏 → 缓存读取 → 缓存渲染 → 网络合并 → 缓存写入

阶段一:骨架屏,0ms 出现

应用壳就绪的那一刻,网络字节还没到。骨架屏立刻上屏。

这不是装饰——这是心理锚点。用户看到骨架,认知从”应用坏了”切换到”应用正在加载”。

阶段二:并行拉缓存

跟网络请求同时进行,从 IndexedDB 拉缓存数据。

别用 localStorage。它是同步的,会阻塞主线程,上限约 5MB。IndexedDB 是异步的、容量可观、跨会话持久化——正好存用户上次会话最近 100 条新闻。

阶段三:缓存上屏

缓存数据一般在几十毫秒内就位。立刻用真实内容替换骨架占位符。

用户看到的是昨天的新闻,但看起来像个真正的应用——而且他们看到了东西。

阶段四:网络合并,无感更新

网络请求完成后,新数据到达。用 Map<id, item> 按新闻 ID 去重,新条目插入列表顶部。

关键:不要自动滚动。 用户可能正在浏览。改成显示一个微妙的”有新内容”提示条,用户想点再点。

阶段五:缓存回写

合并后的数据集写回 IndexedDB,LRU 淘汰策略,上限 100 条。


第二条:虚拟列表不是可选项,而且缓冲区得动态算

100 多条内容的 feed,在低端硬件上渲染每个 DOM 节点完全不可行。

虚拟列表(react-window、vue-virtual-scroller,或手写 IntersectionObserver 实现)只在 DOM 里保留可见行加一小段缓冲区。

大多数教程跳过的关键点:缓冲区大小应该是动态的,不是固定的。

  • 2GB 内存设备:激进缩小预渲染窗口
  • 8GB 旗舰机:放宽缓冲区,追求丝滑

navigator.deviceMemory 或基于初始渲染时间的粗略启发式来决定。在 2GB 设备上套用旗舰机的固定缓冲区,结果就是 OOM 崩溃。


第三条:图片加载的正确姿势——优先级调度

标准建议是”用 IntersectionObserver 懒加载”。问题是:弱连接下,图片请求一旦发起就占住网络槽位直到完成——即使用户已经滚过去了。

结果:三个并发图片下载抢 200KB/s 的带宽。用户正在看的那张最后才加载完。应用感觉慢,不是因为它慢,而是加载顺序错了。

生产级图片加载器做对三件事:

停留时间阈值

卡片进入视口那一瞬间不请求图片。等它稳定可见约 300ms 再请求。

快速滚动中卡片一闪而过——不值得加载。这直接避免了浪费的带宽消耗。

离屏取消

如果卡片在图片加载完成之前滚出视口,用 AbortController 中止 fetch。

在受限网络上,把释放的带宽给当前视口的图片——感知性能提升巨大。

优先级队列

三级调度:

  • 可见区图片:立即派发
  • 预渲染缓冲区:低优先级
  • 已滚出屏幕:直接取消

第四条:弱网络和离线不是”加分项”

三个互补策略,缺一不可:

  • navigator.connection.effectiveType 报告 slow-2g2g 时,自动降级图片质量——请求缩略图而非全分辨率
  • 对图片 fetch 设激进超时,别让卡死的下载永久阻塞队列
  • 网络完全离线时,从 IndexedDB 缓存渲染,图片位用灰色占位块代替

应用永远不应该因为网络不可用就显示空白屏幕。 这不是性能优化——这是基本可用性。


第五条:内存纪律

低端设备在你没注意的时候就崩了。三条规则:

  • 虚拟列表缓冲区根据 navigator.deviceMemory 动态适配
  • 已解码图片位图在配置的 TTL 后从内存 LRU 缓存淘汰
  • 快速滚动期间——通过对比 requestAnimationFrame 各帧时间戳检测——完全跳过图片解码,显示缓存缩略图或占位符。滚动速度降到阈值以下再恢复解码

一句话总结

这些不是学术优化。它们区分了”在开发者的 MacBook Pro 上跑的应用”和”在偏远村庄一格信号的手机上跑的应用”。

在 AI 原生应用里,模型推理已经额外增加了延迟。把前端这些细节丢下不管,等于把你最承受不起的性能损失白白扔在桌上。