前端最离不开探讨的话题就是性能优化,当然面试的时候,面试官最喜欢问的也是"你怎么做的性能优化?性能优化做了什么?......"当听到这些提问的时候,心理一想,又掉进了无底大洞了,回答的好,糊弄过去了,回答的不好,再见👋
接上一篇文章《小程序原理解析》之后,我想接着继续深度来学习小程序的性能优化,并且结合实战,来达到实际与理论结合的最佳体验效果~
背景
在微信小程序开发过程中,项目主要用的是taro,并且团队有多人协作,那么造成后续功能越强大,小程序的性能也慢慢出现了问题,比如小程序经常出现包超大的现象,以及页面加载比较缓慢的情况,那么促使我想要去对这个项目进行优化,来提升开发效率以及用户体验感。并且针对小程序的性能优化,来加深我对此的理解,来看看小程序的性能优化处理和h5的性能优化处理有何不一样呢?
回顾
回顾一下上篇文章中的小程序的双线程运行环境,让逻辑层和渲染层都各司其职,并且通过中间层native进行数据通信。这一个双线程的模式的优点:
-
用户隐私安全问题提升
web端技术存在一些安全隐患,因为js可以通过操作dom来获取页面的信息。同时可能通过js注入脚本的形式来对网页进行一些攻击。比如
xss
攻击~;所以双线程架构下,不允许开发者随意操作 BOM 和 DOM,这对用户的隐私数据具有一定的保护作用,安全水平进一步提升 -
提高性能
在web页面中,在初始化页面dom元素加载的时候,如果遇到js,那么可能会出现阻塞页面渲染,从而去下载js文件,从而影响页面的响应造成白屏或者页面卡顿的现象。使用双线程来进行管理小程序,就不存在 js 堵塞渲染的问题,双线程同时运行,提升性能。
知识储备
工具使用
在微信开发文档中有提到,小程序的性能体验评分是可以通过微信开发者工具中的调试器工具生成的。跟我们网页端的lighthouse
类似,将页面性能评分打出来以及告诉我们影响性能方面的因素有哪些?如何针对这个方面进行整改来提升性能。
由上图可以看出,小程序的性能评分有以下几点:
性能模块评分指标
- 首屏时间;首屏时间不能超过5s
- 渲染时间;渲染时间不超过 500ms
- setData调用频率;每秒调用setData的次数不超过 20 次
- setData数据大小;setData的数据在JSON.stringify后不超过 256KB
- WXML节点数;页面WXML节点少于 1000 个,节点树深度少于 30 层,子节点数不大于 60 个
- 请求耗时;所有网络请求都在 1 秒内返回结果
- 网络请求数:通过wx.request发起的耗时超过 300ms 的请求并发数不超过 10 个
- 图片请求数:同域名耗时超过 100ms 的图片请求并发数不超过 6 个
- 图片缓存:所有图片均开启 HTTP 缓存
- 图片大小:图片宽高乘积 <= 实际显示宽高乘积 * (设备像素比 ^ 2)
- 网络请求缓存;3 分钟以内同一个url请求不出现两次回包大于 128KB 且一模一样的内容
体验模块评分指标
- 开启惯性滚动:wxss中带有overflow: scroll的元素,在 iOS 下需要设置-webkit-overflow-scrolling: touch样式
- 避免使用
:active
伪类来实现点击态:不使用:active伪类,并使用hover-class替换:active - 保持图片大小比例
- iphone X兼容
- 窗口变化适配
- 合理的颜色搭配
最佳实践评分指标
- 避免js异常
- 避免网络请求异常
- 废弃接口
- 使用https
- 避免setData数据冗余
- 最低基础库版本
- 移除不可访问到的页面
- wxss使用率
- 及时回收定时器
wx.performance
获取当前小程序性能相关的信息api
类似我们的网络performance,可以通过api获取网页加载等性能指标的数据。 在这里我们可以了解一下小程序的启动机制。因为小程序的启动也是用户体验中很重要的一个节点,如果启动小程序耗费的时间很长,那么会导致部分用户的流失~
小程序的启动流程可以去微信文档中详细查看,此处不做详细说明了。那么可以大概的说小程序启动流程中有一套机制,会区分首次还是非首次的的情况,来区分是否命中环境预加载,来影响小程序的启动时间。
小程序的启动机制
小程序的启动分为两种:
- 冷启动:如果用户首次打开or被销毁后再次被用户打开,此时小程序需要重新加载启动,「启动」专指冷启动
- 热启动:如果用户已经打开过某小程序,那么在一定时间内再次打开小程序,(小程序未被销毁情况下)。热启动一般叫做切前后台
那在小程序的启动机制下,关于小程序版本更新的情况,【启动】的时候会同步去拉取小程序版本是否更新的情况,如果有版本更新,那么会同步下载最新版本代码,这个时候启动会耗费一些时间来进行代码下载。如果更新失败,那么此时【启动】打开的是本地的旧版本。
之前在我们项目中,有出现后端接口上了,但是前端页面还没上线的情况,导致历史数据和页面不符合。那么这个时候开发者可以手动设置版本强制更新。
js
import { View } from '@tarojs/components';
import Taro, { useDidShow } from '@tarojs/taro';
import Fastlog from './utils/fast';
const Wrap = (props: { children: React.ReactNode }): JSX.Element => {
useDidShow(() => {
forceUpdateVersion();
});
const downLoadAndUpdate = (updateManager) => {
Taro.showLoading();
// 静默下载更新小程序新版本,onUpdateReady:当新版本下载完成回调
updateManager.onUpdateReady(() => {
Taro.hideLoading();
// applyUpdate:强制当前小程序应用上新版本并重启
updateManager.applyUpdate();
});
// onUpdateFailed:当新版本下载失败回调
updateManager.onUpdateFailed(() => {
// 下载新版本失败
Taro.hideLoading();
Taro.showModal({
title: '已有新版本',
content: '新版本已经上线了,请删除当前小程序,重新搜索打开',
});
});
};
// 版本更新处理
const forceUpdateVersion = () => {
// 强制更新版本信息
if (Taro.canIUse('getUpdateManager')) {
const updateManager = Taro.getUpdateManager();
updateManager.onCheckForUpdate((res) => {
if (res.hasUpdate) {
// 检测到有新版本,则需要更新
Taro.showModal({
title: '更新提示',
content: '检测到新版本,是否下载新版本并重启小程序',
success: (success) => {
if (success.confirm) {
Fastlog.report('forceRefreshVersion');
downLoadAndUpdate(updateManager);
} else if (success.cancel) {
Fastlog.report('cancelRefreshVersion');
Taro.showModal({
title: '提示',
content:
'本次版本更新涉及到新功能的添加,旧版本将无法正常使用',
showCancel: false, // 隐藏取消按钮
confirmText: '确认更新', // 只保留更新按钮
success: (confirmRes) => {
if (confirmRes.confirm) {
// 下载新版本,重启应用
downLoadAndUpdate(updateManager);
}
},
});
}
},
});
}
});
} else {
// 在最新版本客户端上体验小程序
Taro.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试',
});
}
};
return(
<View>{props?.children}</View>
);
};
export default Wrap;
小程序的分包
众所周知,小程序规定
整个小程序所有分包大小不能超过20M
单个分包/主包不能超过2M
划分主包和分包需要在app.json文件中进行subpackages
字段的声明。需要将分包的路径划分到该字段结构下。在小程序进行构建的时候才能区分哪些是主包哪些是分包。
那么大概对以上知识点有些了解后,我们开始进入正题
性能优化手段
减小主包和分包的体积大小
从微信开发者工具中可以看到小程序打包后的文件大小
并且点击代码依赖分析可以看到小程序主包和分包对应的大小。 这个时候如何去优化呢:
- 将不是主包的页面划分到分包页面。其实一般除了tabbar的页面以外,其他页面都可以划分分包中。
- 如果是含有静态资源的可以通过脚本上传到cdn中。同时针对小程序有用到图片的地方封装图片组件,在组件里面对图片统一进行裁剪以及图片大小压缩。现有的CDN公司使用的是阿里云的图片存储方式。可以借助阿里云的oss图片处理参数。
- 开发者工具上勾选上传代码自动压缩,及时清理无用代码以及资源文件。
- 插件plugin如果是分包使用,可以尽量放在分包中
js
subpackages: [
{
root: 'modules/user/subPackages',
pages: [
// ...
],
plugins: {
'xxx-plugin': {
version: 'xxx',
provider: 'xxx',
},
},
},
]
// ...
首屏加载优化
- 提前请求不需要等待页面渲染完成的接口。
- 对一些数据进行缓存
- 增加骨架屏的展示来提高用户的体验。
- 避免setData的数据过大,影响页面的卡顿
总结
小程序性能优化其实方法很多,要多通过实战来进行实践。