前端知库

setData 为什么会慢?如何优化小程序的数据更新?

从 Shadow 树规模、更新数据量、调用频率和后台页面四个方面说明 setData 的性能影响因素与官方优化建议。

参考答案

简答

  • 官方列出的两个主要因素是:调用 setData 的组件 Shadow 树节点总量,以及更新的数据量,前者影响更大。
  • 合理拆分组件、控制单个 Shadow 树的大小,是最有效的优化手段。
  • 只传变化的字段,可以用数据路径(如 'list[3].done')更新局部,不要 this.setData(this.data)。
  • data 只放渲染用到的数据,渲染无关的数据挂在实例的其他字段上。
  • 合并连续调用、避免高频调用,页面进入后台后停止更新。

为什么会慢

setData 把逻辑层的数据序列化后发送给视图层,视图层再运行数据更新算法、重新渲染。官方文档指出,这个算法的耗时和 Shadow 树中的节点总量、节点复杂程度显著正相关;更新的数据量则影响数据拷贝和通信耗时。

调用过于频繁(毫秒级)时,逻辑层 JS 线程一直忙碌,无法及时响应用户事件和页面切换;视图层也忙于处理更新,滑动卡顿、操作反馈延迟。

拆分组件,缩小 Shadow 树

页面节点很多时,把频繁更新的部分封装成独立的自定义组件,在组件内部调用 setData。这样每次更新只在小组件的 Shadow 树上运行算法。秒杀倒计时就是典型场景:倒计时放进单独组件,不要在整个页面上每秒更新。

官方还建议对这类区域使用 CSS contain 属性,限制布局、样式和绘制的计算范围。

只传变化的数据

JavaScript
Page({
  data: { list: [] },
  toggle(e) {
    const index = e.currentTarget.dataset.index;
    // 只更新一条记录的一个字段,不重新传整个 list
    this.setData({ [`list[${index}].done`]: !this.data.list[index].done });
  },
});

长列表应分页加载,加载更多时只追加新的条目路径。

data 只放渲染数据

data 中的字段每次更新都会触发渲染流程和跨层通信,因此:

  • WXML 中用到的数据放在 data;
  • 与渲染间接相关的数据可以设为纯数据字段,用 observers 监听变化;
  • 用户 ID、计时器句柄等与渲染无关的数据挂到 this.userData 之类的普通字段上,不要用 data 在方法之间共享状态。

控制频率和后台更新

  • 只在界面需要变化时调用,把同一轮里的多次 setData 合并为一次。
  • 不要在每次页面滚动回调里调用 setData,可以节流或改用 WXS、IntersectionObserver 等方式。
  • 小程序逻辑层是单线程,后台页面的更新会抢占前台页面的资源,用户也看不到。页面隐藏后应暂停倒计时等更新,onShow 时再恢复。

如何定位

组件的 setUpdatePerformanceListener 接口可以获取每次更新的耗时统计,用来找到更新慢的组件。再结合开发者工具的性能面板和真机测试确认结果。

常见追问