uni-app 小程序性能优化
核心知识点
1. 小程序运行机制
了解小程序的运行机制是优化性能的基础:
- 双线程架构:渲染层(WebView)和逻辑层(JavaScriptCore)分离
- 生命周期:启动、显示、隐藏、销毁等阶段
- 内存限制:小程序运行内存有限,需要合理管理
- 网络限制:部分平台对网络请求有并发限制
2. 性能优化策略
有效的小程序性能优化策略包括:
- 启动优化:减少启动时间,提升首屏加载速度
- 渲染优化:提高页面渲染性能,减少卡顿
- 网络优化:优化网络请求,减少数据传输时间
- 内存优化:合理管理内存,避免内存泄漏
- 代码优化:减少代码体积,提高代码执行效率
3. 性能监控工具
常用的小程序性能监控工具:
- 微信开发者工具:性能分析面板
- uni-app 开发者工具:性能监控功能
- 第三方监控平台:如腾讯云、阿里云的小程序监控服务
实用案例分析
案例:小程序性能优化实战
1. 启动优化
优化策略:
- 减少主包体积:使用分包加载,将非核心页面放入分包
- 资源预加载:预加载可能需要的资源和数据
- 缓存策略:合理使用本地缓存,减少重复请求
- 代码压缩:开启代码压缩,减少代码体积
实现代码:
App.vue 中的启动优化:
<script>
export default {
onLaunch() {
// 1. 预加载用户信息
this.preloadUserInfo();
// 2. 预加载常用数据
this.preloadCommonData();
// 3. 检查更新
this.checkUpdate();
},
methods: {
preloadUserInfo() {
// 从缓存获取用户信息
const userInfo = uni.getStorageSync('userInfo');
if (userInfo) {
// 将用户信息存储到全局状态
getApp().globalData.userInfo = userInfo;
}
},
preloadCommonData() {
// 预加载分类数据
uni.request({
url: 'https://api.example.com/categories',
success: (res) => {
if (res.data.code === 0) {
uni.setStorageSync('categories', res.data.data);
getApp().globalData.categories = res.data.data;
}
},
fail: () => {
// 失败时从缓存获取
const categories = uni.getStorageSync('categories');
if (categories) {
getApp().globalData.categories = categories;
}
}
});
},
checkUpdate() {
// 检查小程序更新
if (uni.canIUse('getUpdateManager')) {
const updateManager = uni.getUpdateManager();
updateManager.onCheckForUpdate((res) => {
if (res.hasUpdate) {
updateManager.onUpdateReady(() => {
uni.showModal({
title: '更新提示',
content: '新版本已准备就绪,是否重启应用?',
success: (res) => {
if (res.confirm) {
updateManager.applyUpdate();
}
}
});
});
}
});
}
}
}
};
</script>2. 渲染优化
优化策略:
- 使用虚拟列表:长列表使用虚拟列表,减少DOM节点
- 避免频繁渲染:合理使用
v-if和v-show,避免不必要的渲染 - 使用计算属性:复杂计算使用计算属性,避免重复计算
- 减少 setData:减少
setData调用次数和数据量 - 使用 CSS 动画:优先使用 CSS 动画,避免 JavaScript 动画
实现代码:
虚拟列表组件 (components/virtual-list.vue):
<template>
<view class="virtual-list" :style="{ height: containerHeight + 'px' }">
<view class="list-container" :style="{ transform: `translateY(${offsetTop}px)` }">
<view
v-for="item in visibleItems"
:key="item.id"
class="list-item"
:style="{ height: itemHeight + 'px' }"
>
<slot :item="item"></slot>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
items: {
type: Array,
default: () => []
},
itemHeight: {
type: Number,
default: 100
},
containerHeight: {
type: Number,
default: 500
}
},
data() {
return {
scrollTop: 0,
startIndex: 0,
endIndex: 0,
offsetTop: 0
};
},
computed: {
visibleItems() {
return this.items.slice(this.startIndex, this.endIndex + 1);
},
visibleCount() {
return Math.ceil(this.containerHeight / this.itemHeight) + 2;
}
},
mounted() {
this.updateVisibleRange();
this.bindScrollEvent();
},
methods: {
bindScrollEvent() {
const container = this.$el;
container.addEventListener('scroll', this.handleScroll);
},
handleScroll(e) {
this.scrollTop = e.target.scrollTop;
this.updateVisibleRange();
},
updateVisibleRange() {
this.startIndex = Math.floor(this.scrollTop / this.itemHeight);
this.endIndex = Math.min(this.startIndex + this.visibleCount - 1, this.items.length - 1);
this.offsetTop = this.startIndex * this.itemHeight;
}
}
};
</script>
<style scoped>
.virtual-list {
overflow-y: auto;
position: relative;
}
.list-container {
position: relative;
}
.list-item {
box-sizing: border-box;
border-bottom: 1rpx solid #eee;
padding: 20rpx;
}
</style>使用虚拟列表 (pages/goods/list.vue):
<template>
<view class="goods-list-page">
<virtual-list
:items="goodsList"
:item-height="150"
:container-height="600"
>
<template v-slot:default="{ item }">
<view class="goods-item">
<image :src="item.image" class="goods-image"></image>
<view class="goods-info">
<text class="goods-name">{{ item.name }}</text>
<text class="goods-price">¥{{ item.price }}</text>
</view>
</view>
</template>
</virtual-list>
</view>
</template>
<script>
import VirtualList from '@/components/virtual-list.vue';
export default {
components: {
VirtualList
},
data() {
return {
goodsList: []
};
},
onLoad() {
this.loadGoodsList();
},
methods: {
loadGoodsList() {
// 模拟加载商品列表
const list = [];
for (let i = 0; i < 1000; i++) {
list.push({
id: i,
name: `商品${i}`,
price: Math.random() * 1000,
image: `https://via.placeholder.com/100`
});
}
this.goodsList = list;
}
}
};
</script>
<style scoped>
.goods-list-page {
padding: 20rpx;
}
.goods-item {
display: flex;
align-items: center;
height: 150rpx;
}
.goods-image {
width: 120rpx;
height: 120rpx;
border-radius: 5rpx;
margin-right: 20rpx;
}
.goods-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 120rpx;
}
.goods-name {
font-size: 32rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.goods-price {
font-size: 32rpx;
color: #ff4d4f;
font-weight: 500;
}
</style>3. 网络优化
优化策略:
- 请求合并:合并多个请求,减少网络请求次数
- 数据缓存:合理使用本地缓存,减少重复请求
- 图片优化:使用适当尺寸的图片,支持 WebP 格式
- CDN 加速:使用 CDN 加速静态资源
- HTTP/2:使用 HTTP/2,提高传输效率
实现代码:
网络请求封装 (utils/request.js):
// 网络请求封装
const request = {
// 缓存对象
cache: {},
// 请求方法
async get(url, params = {}, options = {}) {
const { cache = false, cacheTime = 5 * 60 * 1000 } = options;
const cacheKey = url + JSON.stringify(params);
// 检查缓存
if (cache && this.cache[cacheKey]) {
const cachedData = this.cache[cacheKey];
if (Date.now() - cachedData.timestamp < cacheTime) {
return cachedData.data;
}
}
// 发起请求
const res = await uni.request({
url,
method: 'GET',
data: params,
header: {
'Content-Type': 'application/json'
}
});
// 缓存数据
if (cache && res.data.code === 0) {
this.cache[cacheKey] = {
data: res.data,
timestamp: Date.now()
};
}
return res.data;
},
// 并发请求控制
async all(requests) {
return Promise.all(requests);
},
// 清理缓存
clearCache() {
this.cache = {};
}
};
export default request;使用网络请求封装 (pages/index/index.vue):
<script>
import request from '@/utils/request';
export default {
data() {
return {
banners: [],
categories: [],
hotGoods: []
};
},
onLoad() {
this.loadHomeData();
},
methods: {
async loadHomeData() {
try {
// 并发请求,减少请求时间
const [bannersRes, categoriesRes, hotGoodsRes] = await request.all([
request.get('https://api.example.com/banners', {}, { cache: true }),
request.get('https://api.example.com/categories', {}, { cache: true }),
request.get('https://api.example.com/hot-goods', {}, { cache: true })
]);
if (bannersRes.code === 0) {
this.banners = bannersRes.data;
}
if (categoriesRes.code === 0) {
this.categories = categoriesRes.data;
}
if (hotGoodsRes.code === 0) {
this.hotGoods = hotGoodsRes.data;
}
} catch (error) {
console.error('加载首页数据失败:', error);
}
}
}
};
</script>4. 内存优化
优化策略:
- 及时清理定时器:页面卸载时清理定时器
- 避免循环引用:避免对象之间的循环引用
- 合理使用事件监听器:及时移除不需要的事件监听器
- 减少全局变量:减少全局变量的使用,避免内存泄漏
- 使用 WeakMap:对于临时对象,使用 WeakMap 存储
实现代码:
页面中的内存优化 (pages/detail/detail.vue):
<script>
export default {
data() {
return {
timer: null,
scrollListener: null
};
},
onLoad() {
this.startTimer();
this.bindScrollListener();
},
onUnload() {
this.clearTimer();
this.removeScrollListener();
},
methods: {
startTimer() {
// 启动定时器
this.timer = setInterval(() => {
console.log('Timer running...');
}, 1000);
},
clearTimer() {
// 清理定时器
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
bindScrollListener() {
// 绑定滚动事件
this.scrollListener = (e) => {
console.log('Scroll position:', e.scrollTop);
};
window.addEventListener('scroll', this.scrollListener);
},
removeScrollListener() {
// 移除滚动事件
if (this.scrollListener) {
window.removeEventListener('scroll', this.scrollListener);
this.scrollListener = null;
}
}
}
};
</script>5. 代码优化
优化策略:
- 代码分割:使用分包加载,减少主包体积
- 按需加载:按需加载第三方库,减少初始加载时间
- 减少冗余代码:删除无用代码,减少代码体积
- 使用 ES6+ 特性:使用 ES6+ 特性,提高代码执行效率
- 代码压缩:开启代码压缩,减少代码体积
实现代码:
按需加载第三方库:
// 按需加载 lodash
import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle';
// 使用按需加载的函数
export default {
methods: {
// 防抖函数
debouncedSearch: debounce(function(query) {
// 搜索逻辑
}, 300),
// 节流函数
throttledScroll: throttle(function() {
// 滚动逻辑
}, 100)
}
};学习目标
通过本章节的学习,你应该能够:
- 理解小程序运行机制:掌握小程序的双线程架构和生命周期
- 掌握性能优化策略:能够从启动、渲染、网络、内存等方面优化小程序性能
- 使用性能监控工具:能够使用开发者工具和第三方监控平台分析性能问题
- 实现性能优化:能够根据项目特点,实施有效的性能优化方案
- 持续监控性能:建立性能监控机制,持续优化小程序性能
总结
小程序性能优化是一个持续的过程,需要从多个方面入手,综合考虑各种因素。通过合理的优化策略,可以显著提升小程序的用户体验,提高用户留存率。
在实际项目中,我们需要注意以下几点:
- 性能分析:使用性能监控工具,定期分析小程序性能
- 优化优先级:根据用户体验影响程度,确定优化优先级
- 持续优化:建立性能优化的持续迭代机制
- 测试验证:优化后进行充分测试,确保优化效果
- 用户反馈:关注用户反馈,及时解决性能问题
通过本章节的学习和实践,你应该能够熟练掌握 uni-app 小程序性能优化的方法和技巧,为用户提供更加流畅、快速的应用体验。