性能优化是任何应用开发中的重要组成部分,尤其是在移动环境中。对于微信小程序而言,随着用户量的增加和应用功能的丰富,性能优化显得尤为关键。良好的性能不仅提升用户体验,还能增加用户留存率和应用的使用频率。我们将探讨如何在微信小程序中进行性能优化,涵盖从首屏加载、分包、网络请求到渲染性能等多个方面。
- 网络请求:合并请求,减少HTTP请求次数;使用缓存策略减少重复请求。
- 布局与样式:避免使用复杂的CSS选择器;合理使用Flex布局减少重排重绘。
- 事件处理:移除不再需要的事件监听器,防止内存泄漏。
- 组件懒加载、按需加载
一、优化首屏加载
1. 减少初始包的体积
每个小程序都有一个加载包,包含了代码及依赖。为了加快加载速度,我们应尽量减少包的体积。
-
懒加载组件 :在小程序中使用
Component
时,可以选择懒加载。只有在组件实际需要时再加载。javascriptComponent({ properties: { show: { type: Boolean, value: false } }, observers: { 'show': function (newVal) { if (newVal) { // 只在需要展示时加载相关资源 require('./path/to/your/component.js'); } } } });
-
拆分代码 :把代码拆分成多个小模块,按需加载。
合理地分割代码模块,不仅可以使代码结构更加清晰,还能够提高加载效率。只加载用户当前需要的部分,避免一次性加载整个应用的所有代码。
javascript// 示例:按需加载 const pages = { home: () => import('./pages/home'), about: () => import('./pages/about') }; Page({ onLoad: function() { // 根据需要动态加载页面 pages.home().then(module => { module.default.loadContent(); }); } });
-
资源懒加载:按需加载图片和其他资源,减少初始加载时间。
javascript<image src="" mode="widthFix" data-src="{{item.image}}" bindload="imageLoad"/> // JS部分 imageLoad(e) { e.currentTarget.src = e.currentTarget.dataset.src; }
-
资源复用:合理利用已有资源减少重复加载
对于那些多次使用的资源,如图标、背景等,我们可以将其作为公共资源存储起来,避免在每次加载页面时重复下载。
javascript// 示例:将公共图标作为公共资源 const icons = ['icon1.png', 'icon2.png']; function preloadIcons() { icons.forEach(icon => { const img = new Image(); img.src = icon; }); } preloadIcons();
2. 资源压缩与图片优化
-
图片格式与大小:使用合适的格式,如 JPEG、PNG 或 WEBP。尽可能减小图片尺寸,以减少加载时间。借助工具压缩图片,减少图片大小,同时保持较高质量。
-
使用云存储:将图片存储在云端,利用 CDN 加速加载。
-
代码压缩和混淆
利用构建工具(如webpack)进行代码压缩和混淆,减小包体积,提高加载速度。javascript// webpack.config.js module.exports = { mode: 'production', optimization: { minimize: true, }, };
3. 优化 CSS 和 JavaScript
- 减少 CSS 选择器的复杂性:复杂的选择器会增加渲染时间,建议使用简短的类名和 ID。
- 合并 CSS 文件:将多个 CSS 文件合并成一个,减少 HTTP 请求次数。
- 使用 CSS3 动画:使用 CSS3 动画替代 JavaScript 动画,控制性能和流畅度。
二、网络请求优化
网络请求是小程序性能的另一个关键因素,优化网络请求可以显著提升用户体验。
1. 缓存机制
使用缓存机制可以减少数据重复请求,加速数据的获取。
- 使用 wx.setStorageSync 和 wx.getStorageSync:在获取数据后将其存储到本地,以后请求前先检查本地是否已有缓存。
javascript
const fetchData = async () => {
const cachedData = wx.getStorageSync('myData');
if (cachedData) {
return cachedData; // 使用缓存
}
const response = await wx.request({/*...*/});
wx.setStorageSync('myData', response.data);
return response.data;
};
2. 限制请求数量
避免在短时间内发送过多请求,可以合并请求或使用防抖和节流技术。
-
防抖:
javascriptlet debounceTimer; function debounce(func, delay) { return function (...args) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => func.apply(this, args), delay); }; } // 使用示例 const fetchDataDebounced = debounce(fetchData, 300);
-
节流:
javascriptfunction throttle(func, limit) { let lastFunc; let lastRan; return function () { const context = this; const args = arguments; if (!lastRan) { func.apply(context, args); lastRan = Date.now(); } else { clearTimeout(lastFunc); lastFunc = setTimeout(() => { if (Date.now() - lastRan >= limit) { func.apply(context, args); lastRan = Date.now(); } }, limit - (Date.now() - lastRan)); } }; }
3. 分批加载:避免一次性加载大量数据
当需要加载大量数据时,可以采用分批加载的方式,每次只加载一部分数据,待这部分数据处理完毕后再加载下一批。
javascript
// 示例:分批加载数据
function fetchBatchData(page) {
wx.request({
url: `https://api.wwww.com/data?page=${page}`,
success: function(res) {
const newData = res.data;
// 处理数据
this.setData({
items: this.data.items.concat(newData)
});
}
});
}
fetchBatchData(1);
4. 智能请求:根据网络状况调整请求策略
根据用户的网络环境动态调整请求策略,可以进一步提高数据加载的速度和成功率。
javascript
// 示例:根据网络状况调整请求策略
wx.getNetworkType({
success: function(res) {
const networkType = res.networkType;
if (networkType === '2g') {
// 在2G网络环境下减少请求次数
fetchBatchData(1, 5); // 一次性请求5页数据
} else {
// 其他网络环境下正常请求
fetchBatchData(1);
}
}
});
三、优化页面渲染性能
页面渲染效率直接影响用户体验,优化渲染性能是非常必要的。
1. 启动速度优化
- 减少全局数据初始化
- 使用分包加载,将不常用页面分割到子包中
2. 使用 wx:if 和 wx:for
适当使用 wx:if 和 wx:for(使用wx:key
优化列表渲染) 避免过多的视图层重绘。尽量使用 wx:show 替代 wx:if,因为 wx:if 会在条件变化时销毁和重新创建节点。
3. 合理使用数据绑定
在数据大规模更新时,避免一次性改变全部数据,使用 this.setData() 时要尽量精简,减少触发视图的重复渲染。
javascript
this.setData({
'todos[0].completed': true // 只更新部分数据
});
4. 使用组件化
将页面拆分成多个小组件,每个组件负责各自的状态和渲染,这样可以有效地减少失去控制的渲染过程,增加性能。同时也提高了代码的可维护性。
5. 避免长任务
对长时间运行的任务进行拆分,避免在主线程中阻塞 UI 更新。可以使用 Promise 和 setTimeout 进行分步执行,确保 UI 不被冻结。
6. 内存管理
- 监听页面生命周期,及时释放不再使用的资源
- 使用WeakRef避免循环引用问题
javascript
onUnload() {
// 清理定时器、监听器等
this.timer && clearInterval(this.timer);
}
7. 削减冗余:识别与移除无用代码
删除冗余代码就像是给程序减肥,让它变得更加苗条。那些不再使用的函数、未引用的变量或者废弃的样式都应该被清理掉,这样才能保证小程序运行得更快。
javascript
// 示例:删除未使用的变量
let unusedVar = 'I will be removed'; // 不再使用此变量
function cleanUpCode() {
// 删除未使用的变量
delete unusedVar; // 实际开发中应使用工具或手动检查
}
cleanUpCode();
8. 异步处理:合理使用异步任务不阻塞主线程
合理使用异步任务可以避免阻塞主线程,从而提高程序的执行效率。
javascript
// 示例:使用Promise异步加载数据
function fetchDataAsync() {
return new Promise((resolve, reject) => {
wx.request({
url: 'https://api.wwww.com/data',
success: resolve,
fail: reject
});
});
}
fetchDataAsync().then(res => {
this.setData({
items: res.data
});
});
9. 动画优化:流畅过渡增强交互感
通过优化动画效果,可以使用户界面更加流畅,增强用户的交互体验。
javascript
// 示例:优化动画效果
Page({
data: {
animate: false
},
startAnimation: function() {
this.setData({
animate: true
});
setTimeout(() => {
this.setData({
animate: false
});
}, 1000);
}
});
四、减少小程序的启动时间
1. 初始页面优化
在小程序启动时载入的页面要尽量简化,减少初始页面的内容,延迟加载不必要的内容。
2. 使用 wx.navigateTo 与 wx.redirectTo
合理使用页面跳转方式,避免使用 wx.redirectTo 后缀跳转过多的页面,而导致栈溢出,减慢响应速度。
五、工具与监控
1. 性能监控工具
使用微信开发者工具中的性能分析工具来监控小程序的性能,查看每个调用的时间,找出性能瓶颈。
2. 代码性能分析
使用一些 JavaScript 的代码静态分析工具(如 ESLint, Prettier)来确保代码的高效性,及时优化掉低效代码。
六、优化微信小程序超包问题
1. 分包
分包部分规则 :
1、子包最多100个
2、tabBar页面只能在主包中
3、子包内不能再分包
4、子包内的资源不能互相引用(可使用分包异步化解决),但可使用主包内的资源
5、整个小程序所有分包大小不超过 20M,单个分包/主包大小不能超过 2M
小程序开发者工具的代码依赖分析可以查看包体积情况
javascript
// 在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,会把对应分包自动下载下来,下载完成后再进行展示,此时终端界面会有等待提示。
{
// pages 主包(默认启动页面及 TabBar 页面)
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
// 页面上访问路径 /pages/index/index
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
// custom即取消默认的原生导航栏
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
// "enablePullDownRefresh":false, //是否允许底部下拉刷新,可在页面配合 onPullDownRefresh生命周期一起使用
}
},
{
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/cart/cart",
"style": {
"navigationBarTitleText": "购物车"
}
},
{
"path": "pages/404/404",
"style": {
"navigationBarTitleText": "页面未找到",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
// 组件自动引入规则
// easycom方式引入组件不是全局引入,而是局部引入。例如在H5端只有加载相应页面才会加载使用的组件
"easycom": {
// 是否开启自动扫描
"autoscan": true,
"custom": {
// uni-ui 规则如下配置 uni扩展组件
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
//以 Xtx 开头的组件,匹配components目录内的vue文件(需要重启服务器)
"^Xtx(.*)": "@/components/Xtx$1.vue"
// 匹配node_modules内的vue文件,如匹配 uview-ui 包的组件
// "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
}
},
// 底部
"tabBar": {
"color": "#333",
"selectedColor": "#27ba9b",
"backgroundColor": "#fff",
"borderStyle": "white",
"list": [{
"text": "首页",
// pagePath要与上面的pages一致
"pagePath": "pages/index/index",
"iconPath": "static/tabs/home_default.png",
"selectedIconPath": "static/tabs/home_selected.png"
},
{
"text": "购物车",
"pagePath": "pages/cart/cart",
"iconPath": "static/tabs/cart_default.png",
"selectedIconPath": "static/tabs/cart_selected.png"
},
{
"text": "我的",
"pagePath": "pages/my/my",
"iconPath": "static/tabs/user_default.png",
"selectedIconPath": "static/tabs/user_selected.png"
}
// {
// "text": "SKU",
// "pagePath": "pages/sku/sku"
// }
]
},
// 分包页面(需手动创建subPackages文件夹),按需加载
"subPackages": [
{
// 子包的根目录
"root": "pagesMember",
// 页面路径和窗口表现,和上面 pages 一样
// 访问的完整路径 如:/pagesMember/settings/settings
"pages": [{
"path": "settings/settings",
"style": {
"navigationBarTitleText": "设置"
}
},
{
"path": "profile/profile",
"style": {
"navigationBarTitleText": "个人信息",
// 取消默认的导航栏
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
{
"path": "address/address",
"style": {
"navigationBarTitleText": "地址管理"
}
},
{
"path": "address-form/address-form",
"style": {
"navigationBarTitleText": "新建地址"
}
}
]
},
{
"root": "pagesOrder",
"pages": [{
"path": "create/create",
"style": {
"navigationBarTitleText": "结算"
}
},
{
"path": "detail/detail",
"style": {
"navigationBarTitleText": "订单详情",
"navigationStyle": "custom"
}
},
{
"path": "payment/payment",
"style": {
"navigationBarTitleText": "支付结果"
}
},
{
"path": "list/list",
"style": {
"navigationBarTitleText": "订单列表"
}
}
]
}
],
// 预下载分包,分包预下载规则
"preloadRule": {
// 进入 我的 页面,预下载分包
"pages/my/my": {
"network": "all",
// 分包 root的值
"packages": [
"pagesMember"
]
},
"pages/cart/cart": {
"network": "all",
"packages": [
"pagesOrder"
]
}
},
"condition": { //模式配置,仅开发期间生效
"current": 0, //当前激活的模式(list 的索引项)
"list": [{
"name": "", //模式名称
"path": "", //启动页面,必选
"query": "" //启动参数,在页面的onLoad函数里面得到
}]
}
}