微信小程序性能优化 ==== 合理使用 setData & 纯数据字段

目录

[1. setData 的流程](#1. setData 的流程)

[2. 数据通信](#2. 数据通信)

[3. 使用建议](#3. 使用建议)

[3.1 data 应只包括渲染相关的数据](#3.1 data 应只包括渲染相关的数据)

[3.2 控制 setData 的频率](#3.2 控制 setData 的频率)

[3.3 选择合适的 setData 范围](#3.3 选择合适的 setData 范围)

[3.4 setData 应只传发生变化的数据](#3.4 setData 应只传发生变化的数据)

[3.5 控制后台态页面的 setData](#3.5 控制后台态页面的 setData)

纯数据字段

组件数据中的纯数据字段

组件属性中的纯数据字段

使用数据监听器监听纯数据字段

[非data vs 纯数据字段](#非data vs 纯数据字段)

总结


setData 是小程序开发中使用最频繁、也是最容易引发性能问题的接口。

1. setData 的流程

setData 的过程,大致可以分成几个阶段:

  • 逻辑层虚拟 DOM 树的遍历和更新,触发组件生命周期和 observer 等;
  • 将 data 从逻辑层传输到视图层;
  • 视图层虚拟 DOM 树的更新、真实 DOM 元素的更新并触发页面渲染更新。

2. 数据通信

对于第 2 步,由于小程序的逻辑层和视图层是两个独立的运行环境、分属不同的线程或进程,不能直接进行数据共享,需要进行数据的序列化、跨线程/进程的数据传输、数据的反序列化,因此数据传输过程是异步的、非实时的

iOS/iPadOS/MacOS 上,数据传输是通过 evaluateJavascript 实现的,还会有额外 JS 脚本解析和执行的耗时。

数据传输的耗时与数据量的大小 正相关,如果对端线程处于繁忙状态,数据会在消息队列中等待

  • 小程序的逻辑层和视图层是两个独立的运行环境,它们之间的数据传输是异步的、非实时的。
  • 数据传输的耗时与数据量的大小正相关。

3. 使用建议

3.1 data 应只包括渲染相关的数据

setData 应只用来进行渲染相关的数据更新。用 setData 的方式更新渲染无关的字段,会触发额外的渲染流程,或者增加传输的数据量,影响渲染耗时。

  • ✅ 页面或组件的 data 字段,应用来存放和页面或组件渲染相关的数据(即直接在 wxml 中出现的字段);
  • ✅ 页面或组件渲染间接相关的数据可以设置为「纯数据字段」,可以使用 setData 设置并使用 observers 监听变化;
  • ✅ 页面或组件渲染无关的数据,应挂在非 data 的字段下,如 this.userData = {userId: 'xxx'}
  • ❌ 避免在 data 中包含渲染无关的业务数据;
  • ❌ 避免使用 data 在页面或组件方法间进行数据共享
  • ❌ 避免滥用 纯数据字段 来保存可以使用非 data 字段保存的数据。

3.2 控制 setData 的频率

每次 setData 都会触发逻辑层虚拟 DOM 树的遍历和更新,也可能会导致触发一次完整的页面渲染流程。过于频繁(毫秒级)的调用 setData,会导致以下后果:

  • 逻辑层 JS 线程持续繁忙,无法正常响应用户操作的事件,也无法正常完成页面切换;
  • 视图层 JS 线程持续处于忙碌状态,逻辑层 -> 视图层通信耗时上升,视图层收到消息的延时较高,渲染出现明显延迟;
  • 视图层无法及时响应用户操作,用户滑动页面时感到明显卡顿,操作反馈延迟,用户操作事件无法及时传递到逻辑层,逻辑层亦无法及时将操作处理结果及时传递到视图层。

因此,开发者在调用 setData 时要注意:

  • ✅ 仅在需要进行页面内容更新时调用 setData;
  • ✅ 对连续的 setData 调用尽可能的进行合并
  • ❌ 避免不必要的 setData;
  • ❌ 避免以过高的频率持续调用 setData,例如毫秒级的倒计时;
  • ❌ 避免在 onPageScroll 回调中每次都调用 setData。

3.3 选择合适的 setData 范围

组件的 setData 只会引起当前组件和子组件的更新,可以降低虚拟 DOM 更新时的计算开销。

  • ✅ 对于需要频繁更新的页面元素(例如:秒杀倒计时),可以封装为独立的组件,在组件内进行 setData 操作。必要时可以使用 CSS contain 属性限制计算布局、样式和绘制等的范围。

3.4 setData 应只传发生变化的数据

setData 的数据量会影响数据拷贝和数据通讯的耗时,增加页面更新的开销,造成页面更新延迟。

  • ✅ setData 应只传入发生变化的字段;
  • ✅ 建议以数据路径形式改变数组中的某一项或对象的某个属性,如 this.setData({'array[2].message': 'newVal', 'a.b.c.d': 'newVal'}),而不是每次都更新整个对象或数组;
  • ❌ 不要在 setData 中偷懒一次性传所有data:this.setData(this.data)

3.5 控制后台态页面的 setData

由于小程序逻辑层是单线程运行的,后台态页面去 setData 也会抢占前台页面的运行资源,且后台态页面的的渲染用户是无法感知的,会产生浪费。在某些平台上,小程序渲染层各 WebView 也是共享同一个线程,后台页面的渲染和逻辑执行也会导致前台页面的卡顿。

  • ✅ 页面切后台后的更新操作,应尽量避免,或延迟到页面 onShow 后延迟进行;
  • ❌ 避免在切后台后仍进行高频的 setData,例如倒计时更新。

纯数据字段

纯数据字段是一些不用于界面渲染的 data 字段,可以用于提升页面更新性能。从小程序基础库版本 2.8.2 开始支持。

组件数据中的纯数据字段

有些情况下,某些 data 中的字段(包括 setData 设置的字段)既不会展示在界面上,也不会传递给其他组件,仅仅在当前组件内部使用。

此时,可以指定这样的数据字段为"纯数据字段",它们将仅仅被记录在 this.data 中,而不参与任何界面渲染过程,这样有助于提升页面更新性能。

指定"纯数据字段"的方法是在 Component 构造器的 options 定义段中指定 pureDataPattern 为一个正则表达式,字段名符合这个正则表达式的字段将成为纯数据字段。

在开发者工具中预览效果

代码示例:

Component({
  options: {
    pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段
  },
  data: {
    a: true, // 普通数据字段
    _b: true, // 纯数据字段
  },
  methods: {
    myMethod() {
      this.data._b // 纯数据字段可以在 this.data 中获取
      this.setData({
        c: true, // 普通数据字段
        _d: true, // 纯数据字段
      })
    }
  }
})

上述组件中的纯数据字段不会被应用到 WXML 上:

<view wx:if="{{a}}"> 这行会被展示 </view>
<view wx:if="{{_b}}"> 这行不会被展示 </view>

组件属性中的纯数据字段

属性也可以被指定为纯数据字段(遵循 pureDataPattern 的正则表达式)。

属性中的纯数据字段可以像普通属性一样接收外部传入的属性值,但不能将它直接用于组件自身的 WXML 中。

代码示例:

Component({
  options: {
    pureDataPattern: /^_/
  },
  properties: {
    a: Boolean,
    _b: {
      type: Boolean,
      observer() {
        // 不要这样做!这个 observer 永远不会被触发
      }
    },
  }
})

注意:属性中的纯数据字段的属性 observer 永远不会触发!如果想要监听属性值变化,使用 数据监听器 代替。

从小程序基础库版本 2.10.1 开始,也可以在页面或自定义组件的 json 文件中配置 pureDataPattern (这样就不需在 js 文件的 options 中再配置)。此时,其值应当写成字符串形式:

{
  "pureDataPattern": "^_"
}

使用数据监听器监听纯数据字段

数据监听器 可以用于监听纯数据字段(与普通数据字段一样)。这样,可以通过监听、响应纯数据字段的变化来改变界面。

下面的示例是一个将 JavaScript 时间戳转换为可读时间的自定义组件。

在开发者工具中预览效果

代码示例:

Component({
  options: {
    pureDataPattern: /^timestamp$/ // 将 timestamp 属性指定为纯数据字段
  },
  properties: {
    timestamp: Number,
  },
  observers: {
    timestamp: function () {
      // timestamp 被设置时,将它展示为可读时间字符串
      var timeString = new Date(this.data.timestamp).toLocaleString()
      this.setData({
        timeString: timeString
      })
    }
  }
})

<view>{{timeString}}</view>

非data vs 纯数据字段

1.非data字段无法被observers侦听,observers只能侦听properties和data下的属性。

2.在Component自定义组件里也无法在一开始绑定到Component属性上,只能在attached里手动设置,像this.myCustomData = {a: 1},因为除了原生的属性,其他的会被忽略。

纯数据字段既能声明在data里,也能通过pureDataPattern把properties上的字段改成纯数据字段。存数据字段建议用在properties字段上,其他情况用非Data字段

总结

setData

  • data应只包括渲染相关的数据。
  • 控制setData的频率,避免频繁调用。
  • 选择合适的setData范围,比如使用组件的setData可以减少计算开销。
  • setData应只传发生变化的数据。
  • 控制后台态页面的setData,避免不必要的性能浪费。

非data字段

  • 页面或组件渲染无关的数据,应挂在非data的字段下,例如使用this关键字声明的变量。
  • 避免在data中包含渲染无关的业务数据。
  • 避免使用data在页面或组件方法间进行数据共享。
  • 纯数据字段既能声明在data里,也能通过pureDataPattern把properties上的字段改成纯数据字段。存数据字段建议用在properties字段上,其他情况用非Data字段
相关推荐
郭wes代码22 分钟前
Cmd命令大全(万字详细版)
python·算法·小程序
.生产的驴5 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
汤姆yu11 小时前
基于微信小程序的乡村旅游系统
微信小程序·旅游·乡村旅游
计算机徐师兄11 小时前
基于TP5框架的家具购物小程序的设计与实现【附源码、文档】
小程序·php·家具购物小程序·家具购物微信小程序·家具购物
曲辒净11 小时前
微信小程序实现二维码海报保存分享功能
微信小程序·小程序
朽木成才13 小时前
小程序快速实现大模型聊天机器人
小程序·机器人
peachSoda713 小时前
随手记:小程序使用uni.createVideoContext视频无法触发播放
小程序
何极光13 小时前
uniapp小程序样式穿透
前端·小程序·uni-app
小墨&晓末14 小时前
【PythonGui实战】自动摇号小程序
python·算法·小程序·系统安全
oil欧哟1 天前
🤔认真投入一个月做的小程序,能做成什么样子?有人用吗?
前端·vue.js·微信小程序