随着互联网发展,技术提升,对于产品越来越讲究体验了,无论从产品角度的产品体验亦或用户角度的用户体验,页面加载和响应速度成了体验的第一要素。虽然知晓重要性但不会专门做笔记,当看到民间一位前端大佬写《页面加载性能优化》文章时候深深吸引到我的目光,只字不差看了一遍又一遍,看的过程当中回忆工作片刻场景,内心里总会冒起“哦,原来是这样子,底层需求是要我们这样啊~”。
首先当我们浏览网页时,其页面加载速度与用户等待时间比较,页面加载完并完成用户的响应小于0.1秒毫无察觉;小于1秒的延迟不会中断用户的正常思维,但是会被注意到;延迟时间少于10秒,用户会继续等待响应;当超过10秒后,会直接放弃操作关闭当前网页,并留下较差体验印象。
其次,优化重视有必要,但容易重视过度不知道如何写代码了,很容易掉进为了优化而优化陷阱里面,这个触动到我了,码代码时总会想着如何优化美化代码并写的过程中思考有没有做到性能上优化,有过这样子感觉的人应该明白大脑其实是“负重前行”,代码没写几行就容易大脑缺氧,当然并不是排次拒绝,而是拥抱重视,只是用的方法不对,做任何事情都不要想着并行一心多用,否则干啥啥不成,最后给自己无奈贴了标签“一事无成”。所以写代码时不要想着优化,也就是切忌过早优化,把优化看得太重,就容易产生心理误区,也就是手中有锤子,处处是钉子。正确做法应当是:
性能优化遵守木桶原理,即影响系统性能的永远是系统的性能短板。若过早优化,往往头痛医脚,忙手忙脚毫无收获。
经典问题:从访问链接URL到页面展示中间过程经历了什么?
关键回答:浏览器LoadUrl模块解析URL、浏览器dns缓存、电脑本地dns;浏览器网络模块,建立TCP连接、浏览器下载文档,对响应流获取的文档资源解析
浏览器渲染页面注意点:
CSS是阻塞渲染的资源,需要将它尽早尽快地下载到客户端,以便缩短首次渲染的时间。
1. 测量 自动测量(lighthouse)、手动测量(performanceObserver)
2. 收集用户真实数据,并提供可视化展现
3. 分析瓶颈点, 分析代码,并优化代码
. 重复上述步骤
可以看出性能优化是一个不断反馈和优化的闭环,每一个环节都至关重要。下面我们一一分析各个环节。
白屏时间
<head>
<script>var t = new Date().getTime();</script>
<link src="">
<script>
new Date().getTime() - t
</script>
</head>
但是上面这种只能测量首屏有html内容的情况,比如像react这样客户端渲染的方式就不行了。如果采用客户端渲染的方式,就需要在首屏接口返回, 并渲染页面的地方打点记录。
除了HTML内容加载,还有资源加载的时间比如图片资源请求,可以利用onload事件得到图片加载所需的时间。 不过这样的操作太麻烦,推荐使用浏览器performance API来做性能指标测量。
首屏加载时间
首屏时间:就是指用户在没有滚动时候看到的内容渲染完成并且可以交互的时间。
首页时间:也是首页加载时间,则是整个页面滚动到底部,所有内容加载完毕并可交互的时间。用户可以进行正常的事件输入交互操作。
firstscreenready - navigationStart
完全加载时间
DOMContentLoaded 事件,表示直接书写在HTML页面中的内容但不包括外部资源被加载完成的时间,其中外部资源指的是css、js、图片、flash等需要产生额外HTTP请求的内容。 onload 事件,表示连同外部资源被加载完成的时间。
Performance API
日志
性能日志、错误日志、硬件资源日志、业务日志、统计日志
上传埋点信息
将网页相关加载时间信息上报到后端
performance.getEntriesByType('resource').forEach(r => {
console.log(r.name + ': ' + r.duration)
})
另一方面对特定的异步请求接口,打点。对用户所有的交互操作打点(点击,hover等)
const startTime = new Date().getTime()
fetch(url)
.then(res => {
const endTime = new Date().getTime()
report(url, 'success', endTime - startTime)
})
.catch(err => {
const endTime = new Date().getTime()
report(url, 'failure', endTime - startTime)
})
上报轨迹信息
上传轨迹信息就简单了。如果是页面粒度,直接在页面上报就可以了。如果使用了前端路由,还可以在路由的钩子函数中进行上报。
// pageA.js
// 上报轨迹
report('pageA', {userId: '123', meta: {}})
如果用户876521在首页(index),那么ta下一步访问详情页(detail)的概率是50%,访问个人中心(personal-center)的概率为10%,退出页面概率为40%。
{
userId: '123',
pages: {
"index": {
"detail": 0.5,
"return": 0.4,
"personal-center": 0.1
}
}
}
DOM结构减少没有必要的嵌套
提高CSS的加载性能
css的属性继承,currentColor;类的层级不要太深
数据结构,算法优化
V8引擎下的代码优化
内存泄漏
图片优化 压缩图片、减少分辨率、减少位深(位深是用来表示一个颜色的字节数。位深是24位,表达的是使用256(2的24/3次方)位表示一个颜色。因此位深越深,图片越精细。如果可能的话,减少位深可以减少体积)
首次渲染 假设DNS查询和TLS握手需要花费1.6s. 那么我们只剩下5s - 1.6s = 3.4s
1.6s 是根据实际测量结果给出的
国内三大运营商的3G网络数据理论上是350kb/s , 由于地形和周边设施等因素,实际测试平均大约在100kb/s左右。这里以100kb/s计算。
那么我们可以传输的文件大小理论上最大 为 100kb/s * 3.4s = 340kb. 因此我们需要将我们的网站的首屏加载的文件大小总和控制在340kb. 通常来说,控制在170kb以内比较理想。我们按照170kb计算。通常来说gzip对文件大小的压缩比率为5x - 7x.
压缩比率取决于算法本身,文件重复率等
明白了这些基本点,并且我们已经拿到了一组测量的数据。通过这组数据,我们大可以分析出占用时间较长的环节。然后我们就需要一些知识和工具帮助我们正确地找到真正的问题。比如我们可以通过light house来测量数据,然后通过chrome dev tool来分析单次的访问记录。比如我们可以找出耗时较长的任务是什么, 是重排还是JS执行等。然后找到影响的相关代码进行优化,最后别忘了验证。然后拿出前后的对比数据来给自己和大家看。
强烈建议大家将性能检测加入到CI中,然后给项目进行打分。低于一定分数的项目当成是构建失败对待。只有将性能的重要性提到这个高度,我们才能够真正的不断精进,而不是一时之快。市面上这样的工具很多,比如light-house-ci。
加入到CI另一个好处,项目的性能是透明可视化的,这对于管理层了解项目的情况尤为重要。不要小看这一点,很多管理层对于你的各种理论无动于衷, 但是他们对于数字(分数,性能指标)有着很高的兴趣和敏感性。如果你这么做了,那么你更会体会到性能优化远不止技术上的优化, 它伴随着很多其他过程和各方面的取舍。