---
title: 合理使用 setData
summary: "介绍 setData 的合理使用方式，包括控制调用频率、精简传输数据、合并调用和管理后台态更新。"
questions:
  - 为什么非渲染数据不应放在 data 中？
  - 如何控制 setData 的调用频率？
  - setData 路径语法的优势是什么？
  - 为什么应该将多次 setData 合并为一次调用？
  - 组件的 setData 与页面的 setData 在更新范围上有什么区别？
  - 后台态页面调用 setData 会有什么影响？
  - 如何在页面隐藏时停止数据轮询？
  - setData 的数据量如何影响性能？
  - 每次 setData 会触发什么流程？
  - 频繁更新的数据为什么应该下沉到子组件中管理？
docType: default
---

# 合理使用 setData

## 优化原则

### 1. data 中只存放渲染相关的数据

与渲染无关的数据不应放在 `data` 中，否则会触发不必要的渲染和额外的数据传输。

```javascript
// ❌ 避免：非渲染数据放在 data 中
Page({
  data: {
    list: [],
    timer: null,       // 非渲染数据
    cache: {},         // 非渲染数据
    requestId: '',     // 非渲染数据
  }
});

// ✅ 推荐：非渲染数据挂在 this 上
Page({
  data: {
    list: [],
  },
  onLoad() {
    this.timer = null;       // 不触发渲染
    this.cache = {};         // 不触发渲染
    this.requestId = '';     // 不触发渲染
  }
});
```

### 2. 控制 setData 的频率

每次 `setData` 都会触发逻辑层虚拟 DOM 树的遍历和更新，也可能会导致触发一次完整的页面渲染流程。高频调用（如滚动事件中）应使用节流控制。

```javascript
// ❌ 避免：高频更新 data
Page({
  data: { scrollTop: 0 },
  onPageScroll(e) {
    this.setData({ scrollTop: e.scrollTop }); // 每次滚动都触发
  }
});

// ✅ 推荐：使用节流控制更新频率
Page({
  data: { scrollTop: 0 },
  onLoad() {
    this._throttleTimer = null;
  },
  onPageScroll(e) {
    if (this._throttleTimer) return;
    this._throttleTimer = setTimeout(() => {
      this.setData({ scrollTop: e.scrollTop });
      this._throttleTimer = null;
    }, 100);
  }
});
```

### 3. setData 只传发生变化的数据

`setData` 的数据量会影响数据拷贝和数据通讯的耗时。应只传递真正变化的字段，并使用路径语法精确更新。

```javascript
// ❌ 避免：传入整个对象
this.setData({ list: newList });

// ✅ 推荐：使用路径语法精确更新单项
this.setData({ 'list[0].name': '新名称' });
```

### 4. 合并多次 setData

多个相关的数据更新合并为一次 `setData` 调用，避免多次触发渲染：

```javascript
// ❌ 避免：多次 setData
this.setData({ loading: true });
this.setData({ list: [] });
this.setData({ total: 0 });

// ✅ 推荐：合并为一次
this.setData({ loading: true, list: [], total: 0 });
```

### 5. 选择合适的 setData 范围

组件的 `setData` 只会引起当前组件和子组件的更新，可以降低虚拟 DOM 更新时的计算开销。将频繁更新的数据下沉到子组件中管理，避免页面级别的整体更新。

### 6. 控制后台态页面的 setData

由于小程序逻辑层是单线程运行的，后台态页面去 `setData` 也会抢占前台页面的运行资源，且后台态页面的渲染用户是无法感知的，会产生浪费。

```javascript
// ✅ 推荐：页面隐藏时停止数据轮询
Page({
  onShow() {
    this._isActive = true;
    this._startPolling();
  },
  onHide() {
    this._isActive = false;
    if (this._timer) {
      clearInterval(this._timer);
      this._timer = null;
    }
  },
  _startPolling() {
    this._timer = setInterval(() => {
      if (this._isActive) {
        fetchLatestData().then(res => {
          this.setData({ data: res });
        });
      }
    }, 3000);
  }
});
```

## 检查清单

| 检查项 | 问题表现 | 优化方法 |
| ------ | -------- | -------- |
| data 中是否有非渲染数据 | 不必要的渲染开销 | 挂在 this 上 |
| 是否有高频 setData | 操作卡顿 | 使用节流/防抖 |
| 是否传输了大量数据 | 通信耗时长 | 只传变化的字段，使用路径语法 |
| 是否多次分散调用 setData | 多次渲染 | 合并为一次调用 |
| 后台页面是否有 setData | 资源浪费 | 页面隐藏时停止更新 |
