小程序性能优化实战:setData 卡顿与长列表渲染提速

14 ℃ 0 条
内容摘要小程序页面卡顿,多数能追到 setData 头上。它既是逻辑层到视图层唯一的通信通道,也是序列化加跨线程传输的开销。本文先讲清它慢在哪,再给出精准路径更新与合并写入两种写法,最后解决长列表一次渲染上千条数据的卡顿,附可直接复制的虚拟列表实现。
小程序性能优化实战:setData 卡顿与长列表渲染提速

小程序页面卡顿,十次有八次能追到 setData 头上。它是逻辑层到视图层唯一的通信通道,每次调用都伴随序列化与跨线程传输。本文讲清它慢在哪,给出精准更新与合并写入两种写法,再解决长列表一次渲染上千条数据的卡顿。

一、setData 卡顿的根源

1. 跨线程通信与全量 diff

小程序采用双线程模型:逻辑层跑在 JSCore,视图层跑在 WebView,两者不共享内存。setData 的链路是——序列化数据、经 JSBridge 跨线程传输、视图层反序列化后执行 diff 更新节点。每次调用至少含这三步,数据量越大耗时越长。

2. 两种典型错误写法

第一种是把整个 data 原样回填(this.setData({ list: this.data.list })),值没变也触发完整通信:

for (let i = 0; i < 100; i++) {
  this.setData({ [`list[${i}].checked`]: true })
}

第二种是在循环里反复调用,100 次调用就是 100 次跨线程通信加 100 次 diff,滚动时主线程被占满,掉帧就是这样来的。

二、setData 的正确写法

1. 用数据路径精准更新

只有显式写进 setData 参数的路径才会被传输,用 list[3].title 这类语法时视图层只收对应字段,通信量明显下降:

this.setData({ 'list[3].title': '新标题', count: this.data.count + 1 })

改某条记录时优先用路径语法,别为改一个字段重拼整个数组——后者传的是全量数组。

2. 合并写入与节流

把多次修改合并成一次调用,成本最低。循环场景先在内存里组好补丁对象,最后统一提交:

const patch = {}
for (let i = 0; i < 100; i++) { patch[`list[${i}].checked`] = true }
this.setData(patch)

对 scroll、input 这类高频事件,用节流把频率压到 100ms 一次:onScroll 里先 clearTimeout 再 setTimeout 提交 scrollTop,别每帧都通信。

3. 别把不需要渲染的数据塞进 data

接口原始响应、缓存用的 Map 等只算不渲染的字段,不要放进 data,改挂在 this.rawData 上,就不会进入序列化。

三、长列表渲染优化

1. 分页加载与虚拟列表

一次渲染 1000 条节点,首屏会卡住 1–2 秒。数据超过 100 条就该分页,滚到底再追加;整屏展示时用虚拟列表只渲染可视区附近的节点:

const itemHeight = 88
const visible = Math.ceil(600 / itemHeight) + 10
const start = Math.max(0, Math.floor(scrollTop / itemHeight) - 5)
this.setData({ renderList: this.data.list.slice(start, start + visible), offsetTop: start * itemHeight })

WXML 里用 translateY 加一条占位撑起总滚动高度,可视区外的节点不进渲染树,节点数降到二十几个。

2. 压缩节点层级与图片开销

列表项尽量少包 view,每多一层多一次样式计算。图片统一用 image 组件并写好固定宽高与 mode=&quot;aspectFill&quot;,防止加载后触发重排。

关键配置提示

  • scroll-view 开 enhanced,iOS 滚动更顺滑
  • 列表项必加 wx:key,避免 diff 时按索引错位复用节点
  • 非渲染数据挂在 this 上,不进 data

发表评论 (已有0条评论)

快来评论,快来抢沙发吧~