性能问题的三个来源

小程序的性能问题,用户往往只说得出「卡」和「慢」两个词,但在工程上它们对应完全不同的环节。把它拆开看,来源基本落在三处:启动链路(从点开小程序到首屏可交互)、渲染链路(数据变化到界面更新)、交付体积(用户要为这次访问下载多少代码与资源)。三者互相牵制,只优化其中一环,体验提升很快就会触到天花板。

先建立度量再谈优化,是所有性能工作里性价比最高的一步。开发者工具的性能面板能拿到启动耗时、页面渲染耗时与内存占用,再配合基础库的性能接口把真实用户数据上报,你才有「改前改后到底差多少」的判断依据。没有基线的优化,多数时候只是把感觉当成了证据。

性能不是某个接口的开关,而是一条从点击到呈现的完整链路。哪一环最慢,就先修哪一环。
本文核心知识点
  • 冷启动代码包下载、注入执行与首屏渲染共同决定首次打开速度。
  • setData跨线程通信有成本,数据越少越小、越晚发送越好。
  • 分包加载把主包压到限制以内,其余业务按需拉取。
  • 持续监控用真实用户数据看分位数,而不是只看平均值。
小程序性能优化示意:速度表盘、层叠代码块与分包模块组成的蓝调扁平科技插画
性能治理是一条链路工程:先定位瓶颈,再按代价与收益排序动手。

启动性能:冷启动与首屏

冷启动指小程序进程尚未存在时的一次完整加载,它由三段耗时叠加而成:代码包下载、逻辑注入与执行、首屏渲染。热启动则复用已有进程,通常只做页面切换,快得多。用户抱怨的「打开慢」,九成说的是冷启动,所以度量要盯住这三段分别花了多久。

阶段主要耗时来源常见优化手段
代码包下载包体积大、弱网环境分包、去除无用资源、预下载
逻辑注入执行同步初始化任务过多延后非关键初始化、拆分同步逻辑
首屏渲染首屏接口串行、依赖过多骨架屏、本地兜底数据、请求并行与缓存

首屏优化有一条朴素但有效的原则:先渲染结构,再填充数据。进入页面时先把骨架与静态文案画出来,让用户马上看到反馈;接口返回后再替换为真实内容。同时把多个互不依赖的请求并行发出,别让它们排队等前一个的结果。

还有一点容易被忽略:全局的 app.js 里如果塞了太多同步逻辑,比如同步读取大量本地缓存、同步计算字典,它会挡住所有页面。把这些任务挪到首个页面渲染完成之后再跑,或者干脆改成异步,冷启动时间往往能立竿见影地下降。

渲染性能:setData 与长列表

小程序采用双线程模型:逻辑层跑在 JSCore,视图层负责渲染,两者之间靠 setData 传递数据。这意味着每次调用都要把数据序列化后跨线程传输,数据越大、频率越高,卡顿越明显。优化的核心思路只有一句:只传变化的部分,尽量少传、尽量晚传。

// 只更新变化的那一项,避免整表重渲染
const idx = 3;
this.setData({
  ["list[" + idx + "].done"]: true,
  progress: 42
});

路径写法能让框架精确知道哪一项变了,从而只刷新对应的节点。相比之下,把整张列表重新赋值一次,视图层就要重建全部节点,代价差了好几倍。几条经验规则值得记住:

  • 不要把与渲染无关的数据放进 data,需要保留就挂在页面实例的其他字段上。
  • 滚动、拖拽这类高频事件里避免连续调用 setData,先节流或合并成一次。
  • 长列表用分页或虚拟列表,控制同时在屏的节点数量。
  • 图片给定固定宽高并开启懒加载,避免加载完成时页面跳动。

内存同样要盯。页面销毁时记得清掉定时器、事件监听与大数据引用,否则反复进入离开会让内存只涨不降,最终触发闪退。这类问题在真机上比在开发者工具里更容易复现。

包体积:分包与依赖瘦身

包体积直接影响下载时间,平台对它有硬性限制:主包通常要求不超过 2MB,整个小程序所有分包合计也有上限。当业务越做越大,把全部代码塞进主包迟早会撞线,这时就要靠分包把体积拆开。

{
  "pages": ["pages/index/index", "pages/mine/mine"],
  "subPackages": [
    {
      "root": "packageA",
      "pages": ["pages/detail/detail", "pages/list/list"]
    }
  ],
  "preloadRule": {
    "pages/index/index": { "network": "all", "packages": ["packageA"] }
  }
}

分包的基本约定是:主包放启动页面与公共代码,其余业务按模块拆到分包里。用户访问不到的分包不会被下载,首屏体积自然变小。配置里的 preloadRule 用来做分包预下载,用户还在看首页时,让常用的分包在后台悄悄下好,跳转时就不会白屏等待。

  • 公共组件与工具库放主包,避免每个分包各存一份造成重复。
  • 把低频功能,如设置、帮助、历史记录,拆到独立分包。
  • 清理未引用的图片与依赖,体积往往能一次性瘦下来不少。

资源与网络优化

体积不只来自代码,图片、字体与静态资源常常才是大头。一张未压缩的高清图动辄几百 KB,列表里放上十几张,流量和渲染压力都会陡然上升。

资源类型常见问题优化手段
图片尺寸过大、格式陈旧按显示尺寸裁剪、改用 WebP、接入 CDN 压缩
网络请求串行、缺少缓存请求并行、合并、设置合理缓存策略
本地存储同步接口阻塞主线程优先异步接口,控制单次读写数据量

网络这一层,优先做两件事:减少请求数和用上缓存。能合并的接口合并,能缓存的列表页结果就别每次重新拉。首屏只请求必须的数据,剩余内容滚动到可见区域再加载,用户感知到的速度会明显不同。