微信小程序性能优化
一、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 结构);
- 合理利用缓存和分包(减少网络请求和包体积);
- 避免主线程阻塞(延迟非必要任务、优化动画)。
以上代码可直接在小程序中使用,根据实际业务场景调整参数即可。