如何收集和评估首屏性能优化的效果
针对用户访问的关键路径,比如从首页跳转到列表页、从首页跳转到个人中心、从列表页跳转到商详页等,哪些关键页面需要做首屏优化,确定下来范围。
制定每个页面场景的首屏优化目标,比如打开首页,现阶段在低端机上花了2000ms,只要首屏优化到1000ms内就算达标(低端机容易出现性能问题,高端机在性能问题上的表现不明显)。
测试人员根据关键路径,使用低端机操作页面转场,并进行视频录制,将1秒划分为60帧,即每帧约16.7毫秒(为了方便计算,可以改为每一帧20毫秒)。由于网络等影响因素比较多,每一次录制得出的数据都不一样,测试人员需要重复录制10次,去掉10次中的异常数值后取平均值,该平均值作为本次统计的耗时。
举个例子,视频中在第915帧点击了列表页商品,开始转场跳商详,在第970帧时商详页展示主UI,那么耗时为(970-915)*20=1100ms
。
另外,测试人员需要在发版前、发版后都进行录制,要求发版前录制主要是为了避免性能问题暴露到生产环境上。
排查工具使用
git
有时候发版前,测试人员跟你说xx页面的性能数据比前一天的上涨了几百毫秒,你可以通过指定目录下的24小时内git commit内容改动,快速定位有性能问题的代码改动。
Performance
可以通过谷歌浏览器的Performance面板录制具体页面的trace,进行分析,记得把cpu降速6x
Memory
可以用来排查内存泄漏,先点击强制进行垃圾回收,然后观察指标是否大幅上升
资源的优化
打包工具的资源分析插件
可以用上述插件进行包体积的分析,做针对性的优化,比如js chunk拆分等。举个常见案例,常量的代码如果和工具方法的代码都写在一个js文件里,常量在多个页面被使用,而工具方法只在一个页面被使用,打包时常量和工具方法被打进一个chunk里,其他页面引用该chunk有多余资源浪费。
资源的预取
<link rel="preload" href="https://xx.com/a.webp" as="image">
preload表示资源的优先级最高,比如可以把首屏加载的图片设置为preload。
<link rel="prefetch" href="https://yy.com/915443.js" as="script">
prefetch表示该资源在当前页面用不到,但是下个页面可能会用到,会等当前其他资源加载好了之后,下载队列空闲,该资源才会被下载缓存起来。比如首页跳转列表页,在首页设置prefetch列表页的主chunk。github.com/GoogleChrom... ,quicklink这个库是跟prefetch有关的。
<link rel="preconnect" href="https://cc.com">
preconnect的作用是提前和目标服务器进行连接,比如提前和图片服务器连接好。
<link rel="dns-prefetch" href="https://dd.com">
dns-prefetch的作用类似preconnect
script async: 解析html时,如果遇到标识为async的script标签,不会阻塞解析HTML的剩余代码,会异步请求加载该js脚本,但是脚本加载完成之后会阻塞停止解析html,立即执行脚本,最后等执行完脚本再继续解析html代码。
script defer: 解析html时,如果遇到标识为defer的script标签,不会阻塞解析HTML的剩余代码,会异步请求加载该js脚本,但是脚本加载完成之后不会立即执行脚本,而是等解析完html代码再执行该js脚本。
图片的优化
图片自动转webp
在首页、列表页等ssr页面,判断设备是否支持webp,将结果写入cookie中,这样做可以在node端读取cookie判断,支持webp的话就进行转换(需要图片服务器同时提供png、webp等格式)
js
const isSupportWebp = function () {
if (typeof window === 'undefined') {
return false
}
const $canvas = document.createElement('canvas')
const data = $canvas && $canvas.toDataURL('image/webp', 0.5)
return data.indexOf('data:image/webp') === 0
}
const transformWebp = function (imgUrl) {
const imgHosts = ['img1.com', 'img2.com']
const canTransform = imgHosts.some(host => imgUrl.includes(host))
if (canTransform) {
return imgUrl.replace(/\.(jpg|jpeg|png)$/, '.webp')
}
return imgUrl
}
图片手动预取
比如我们可以在首页空闲时主动预取一些个人中心的图片,当跳到个人中心时图片就是从缓存中读取
js
export const preloadImgs = async (imgs = [], timeout = 3000) => {
return Promise.race([
new Promise((resolve) => {
let loadedNum = 0
const imgLoadHandle = () => {
loadedNum++
if(loadedNum === imgs.length) {
resolve()
}
}
imgs.forEach((src) => {
const img = new Image()
img.src = src
img.onload = imgLoadHandle
img.onerror = imgLoadHandle
})
}),
new Promise((resolve) => {
setTimeout(() => {
resolve()
}, timeout)
})
])
}
在测试图片的预取是否生效时记得不要开启浏览器Network面板的disable cache,开启了会导致图片缓存不生效。
lazyload
非首屏图片设置lazyload
图片裁切
手机宽度有很多种宽度,ipad设备宽度一般比手机宽度大很多,根据设备宽度获取不同size的图片(图片服务器需要提供不同size的图片),比如375宽度只需要750宽度的图片,不需要获取1366宽度图片,750宽度图片比1366宽度图片小很多。
更多内容看第二篇(第二篇还没写完)