一、setData
优化(减少通信开销)
问题:频繁调用 setData
或传递大量数据会导致性能卡顿
优化前(错误示例):
// 循环中多次调用 setData,性能差
for (let i = 0; i < 100; i++) {
this.setData({
[`list[${i}]`]: { id: i, name: `item${i}` }
});
}
// 传递无关数据(整个对象)
this.setData({
user: { name: '张三', age: 20, address: '...' } // 实际只修改了 name
});
优化后(正确示例):
// 1. 合并修改,减少调用次数
const newList = [];
for (let i = 0; i < 100; i++) {
newList.push({ id: i, name: `item${i}` });
}
this.setData({ list: newList }); // 一次调用完成
// 2. 只传递变化的字段
this.setData({
'user.name': '张三' // 仅更新修改的字段
});
// 3. 避免在 data 中存储无需渲染的数据
// 用 this 直接存储非渲染数据(不参与页面渲染)
this.tempData = { log: '临时日志', timer: 123 }; // 不会触发渲染
二、分包加载(解决包体积过大)
问题:主包体积超过 2MB 导致无法发布
优化方案: 在 app.json
中配置分包,将非核心页面拆分到分包
{
"pages": [
"pages/index/index", // 主包页面(首页必须在主包)
"pages/login/login"
],
"subpackages": [
{
"root": "packageA", // 分包A
"pages": [
"pages/detail/detail", // 路径:packageA/pages/detail/detail
"pages/setting/setting"
]
},
{
"root": "packageB", // 分包B
"pages": ["pages/help/help"]
}
],
"preloadRule": {
// 进入首页后,预加载 packageA(提升用户访问分包页面的速度)
"pages/index/index": {
"network": "all", // 所有网络环境都预加载
"packages": ["packageA"]
}
}
}
三、虚拟列表(优化长列表渲染)
问题:列表数据量过大(如 1000 条)导致渲染卡顿
优化方案: 使用 recycle-view
只渲染可视区域内的列表项
<!-- pages/list/list.wxml -->
<view class="list-container">
<recycle-view
class="recycle-list"
height="600rpx" <!-- 固定高度,用于计算可视区域 -->
width="100%"
buffer-size="5" <!-- 上下预加载5个项 -->
data-key="id" <!-- 数据唯一标识字段 -->
bind:itemtap="onItemTap"
>
<!-- 列表项模板 -->
<view slot="item" class="list-item">
<text>{{ item.name }}</text>
</view>
</recycle-view>
</view>
// pages/list/list.js
Page({
onLoad() {
// 1. 初始化虚拟列表
this.recycleList = this.selectComponent('.recycle-list');
// 2. 模拟1000条数据
const bigData = [];
for (let i = 0; i < 1000; i++) {
bigData.push({ id: i, name: `项目 ${i + 1}` });
}
// 3. 加载数据到虚拟列表(仅渲染可视区域)
this.recycleList.setData({
list: bigData
});
}
});
四、启动优化(减少白屏时间)
问题:首次启动时初始化逻辑过多导致白屏
优化方案: 延迟加载非必要数据 + 骨架屏
<!-- pages/index/index.wxml(骨架屏示例) -->
<view class="container">
<!-- 骨架屏(页面加载完成前显示) -->
<view wx:if="{{!isLoaded}}" class="skeleton">
<view class="skeleton-title"></view>
<view class="skeleton-list">
<view class="skeleton-item"></view>
<view class="skeleton-item"></view>
</view>
</view>
<!-- 实际内容(加载完成后显示) -->
<view wx:else>
<text>{{ title }}</text>
<view wx:for="{{ list }}">{{ item.name }}</view>
</view>
</view>
// app.js
App({
onLaunch() {
// 1. 优先加载核心数据(如用户登录状态)
this.checkLoginStatus();
// 2. 延迟加载非核心数据(利用 setTimeout 放到下一帧)
setTimeout(() => {
this.loadRecommendData(); // 推荐内容可延后加载
this.initAnalytics(); // 统计分析可延后初始化
}, 0);
},
checkLoginStatus() {
// 核心逻辑:检查登录状态
wx.getStorage({
key: 'token',
success: (res) => { /* 已登录逻辑 */ },
fail: () => { /* 未登录逻辑 */ }
});
}
});
五、缓存策略(减少重复请求)
问题:频繁请求相同数据导致网络开销大
优化方案: 缓存接口数据,设置过期时间
// utils/request.js(封装带缓存的请求工具)
function requestWithCache(url, options = {}) {
const { cacheTime = 300000 } = options; // 默认缓存5分钟
const cacheKey = `cache_${url}`;
// 1. 先查缓存
return new Promise((resolve) => {
wx.getStorage({
key: cacheKey,
success: (res) => {
const { data, timestamp } = res.data;
// 缓存未过期,直接返回缓存数据
if (Date.now() - timestamp < cacheTime) {
return resolve(data);
}
},
complete: () => {
// 2. 缓存过期或无缓存,发起请求
wx.request({
url,
...options,
success: (res) => {
// 3. 缓存请求结果
wx.setStorage({
key: cacheKey,
data: { data: res.data, timestamp: Date.now() }
});
resolve(res.data);
}
});
}
});
});
}
// 使用示例
requestWithCache('https://api.example.com/list', { cacheTime: 600000 })
.then(data => {
console.log('数据:', data);
});
六、动画优化(避免卡顿)
问题:用 setData
驱动高频动画导致掉帧
优化方案: 使用 wx.createAnimation
或 CSS 动画
// 优化前(用 setData 驱动动画,性能差)
let left = 0;
setInterval(() => {
left += 10;
this.setData({ ballLeft: left }); // 高频调用 setData
}, 16);
// 优化后(用 createAnimation,在渲染层执行动画)
const animation = wx.createAnimation({
duration: 1000,
timingFunction: 'linear'
});
// 循环动画
setInterval(() => {
animation.translateX(300).step();
animation.translateX(0).step({ duration: 0 }); // 复位(无动画)
this.setData({ animationData: animation.export() });
}, 1000);
总结
小程序性能优化的核心思路是:
- 减少
setData
通信成本(合并调用、精简数据); - 控制渲染节点数量(虚拟列表、简化 DOM 结构);
- 合理利用缓存和分包(减少网络请求和包体积);
- 避免主线程阻塞(延迟非必要任务、优化动画)。
以上代码可直接在小程序中使用,根据实际业务场景调整参数即可。