# 微信小程序优化思路

这里的小程序特指微信小程序

# 指标

  • FCP:白屏加载结束;
  • FMP:首屏渲染完成;
  • TTI:所有内容加载完成;

# 官方性能指标

主要围绕 渲染表现setData 数据量元素节点数 和 网络请求延时 这几个维度来给予定义;

  • 首屏时间不超过 5 秒;
  • 渲染时间不超过 500ms;
  • 每秒调用 setData 的次数不超过 20 次;
  • setData 的数据在 JSON.stringify 后不超过 256kb;
  • 页面 WXML 节点少于 1000 个,节点树深度少于 30 层,子节点数不大于 60 个;
  • 所有网络请求都在 1 秒内返回结果;

微信小程序性能指标详解 (opens new window)

# 性能分析工具

# 小程序底层架构

不同于传统浏览器单线程,小程序采用双线程模型。

  • 视图层:Webview 线程,启用不同 webview 来渲染不同的小程序页面
  • 逻辑层:单独的线程执行 JS 代码,可以控制视图层的逻辑

优势:不会经常出现阻塞情况

任何线程间的数据传输都是有延时的,意味着逻辑层和视图层间通信是异步行为 **

# 启动慢

启动过程:

  • 准备运行环境:启动双线程环境,并在线程中完成小程序基础库的初始化和预执行
  • 下载小程序代码包:下载编译后的代码包到本地,有缓存优先读取缓存
  • 加载小程序代码包:基础库会完成所有页面的注册
  • 初始化小程序首页

优化:

  • 无用文件,函数,样式剔除
  • 减少代码包中静态资源文件:图片启用CDN
  • 逻辑后移,精简前端逻辑:不涉及前端计算展示类逻辑,都可后移
  • 复用模板插件
  • 分包加载
  • 部分页面 H5 化

# 白屏时间长

影响因数:

  • 网络资源加载时间
  • 渲染时间

优化

  • 启用本地缓存
  • 数据预拉取 (opens new window)
  • 跳转时预拉取
  • 分包预下载 (opens new window)
  • 非关键渲染数据延迟请求
  • 分屏渲染
  • 接口聚合,请求合并
  • 骨架屏
  • 图片资源优化
    • 使用 WebP 格式
    • 图片裁剪&降质
    • 图片懒加载,雪碧图优化
    • 降级加载大图资源:我们可以先呈现高度压缩的模糊图片,同时利用一个隐藏的  节点来加载原图,待原图加载完成后再转移到真实节点上渲染

注意,具有 display: none 样式的  标签只会加载图片资源,但不渲染。

<!-- banner.wxml -->
<image src="{{url}}" />

<!-- 图片加载器 -->
<image
  style="width:0;height:0;display:none"
  src="{{preloadUrl}}"
  bindload="onImgLoad"
  binderror="onErrorLoad"
/>
1
2
3
4
5
6
7
8
9
10
// banner.js
Component({
  ready() {
    this.originUrl = 'https://path/to/picture'  // 图片源地址
    this.setData({
      url: compress(this.originUrl)             // 加载压缩降质的图片
      preloadUrl: this.originUrl                // 预加载原图
    })
  },
  methods: {
    onImgLoad() {
      this.setData({
        url: this.originUrl                       // 加载原图
      })
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 渲染性能

跳转页面过程:

  • 准备新的 webview 线程环境,包括基础库的初始化;
  • 从逻辑层到视图层的初始数据通信;
  • 视图层根据逻辑层的数据,结合 WXML 片段构建出节点树(包括节点属性、事件绑定等信息),最终与 WXSS 结合完成页面渲染;

由于微信会提前开始准备 webview 线程环境,所以小程序的渲染损耗主要在后两者 数据通信 和 节点树创建/更新 的流程中。相对应的,比较有效的渲染性能优化方向就是:

  • 降低线程间通信频次
  • 减少线程间通信的数据量
  • 减少 WXML 节点数量

优化

  • 合并 setData  调用
  • 只把渲染相关的数据放在 data  中
  • 应用层的数据 diff
  • 组件层的 diff
  • 去掉不必要的事件绑定
  • 去掉不必要的节点属性
  • 适当的组件颗粒度
  • 事件总线,替代组件间数据绑定的通信方式:eventBus
// 全局事件调度
class EventBus {
  constructor() {
    this.events = {}
  }

  on(key, cb) { this.events[key].push(cb) }

  trigger(key, args) { 
    this.events[key].forEach(function (cb) {
      cb.call(this, ...args)
    })
  }
  
  remove() {}
}

const event = new EventBus()

// 事件订阅者 子组件
Component({
  created() {
    event.on('data-ready', (data) => { this.setData({ data }) })
  }
})

// 事件发布着 Parent
Component({
  ready() {
    event.trigger('data-ready', data)
  }
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 内存高

当小程序占用系统资源过高,就有可能会被系统销毁或被微信客户端主动回收 **

参考:

更新于: 3/27/2020, 5:50:56 AM