突然有一天,你的老板突然对你说页面加载太慢了。你非常紧张,马上在网上搜索如何优化前端性能,结果出现了各种类似的文章:《前端性能优化7大建议》、《9种方法让你的页面变快》、《前端性能优化之雅虎35条军规》等等。这些文章讲解得头头是道,涉及 CDN 优化、懒加载等各种优化方法。然而,你也许会产生一个疑问:这些优化方法真的能够显著提高我的页面性能吗?
我知道你很急,但先别急。就像去医院看病一样,医生通常也不会立刻给你开药,而是先通过望、闻、问、切等一系列诊断来找出病因所在。性能优化也是如此,首先进行前端性能评估,找出页面的性能瓶颈,确定哪些优化才是性价比最高的。然后再集中资源逐步进行优化。
性能评估报告的关键在于"报告"二字,这是一份要向他人说明的报告,所以需要简洁、突出重点。本文的基本架构是先列出报告的内容,然后再说明为什么要这么做报告。
结论
报告结论:
- 字节剪映详情页的
LCP 时间为3.5秒
,略高于推荐值2.5秒,还存在优化空间。 - 性能瓶颈出现在后台接口请求阶段,
耗时2秒
。首屏接口请求数量为41条,但实际只请求了82kb的数据。 - JavaScript 包的传输体积为
1.7MB
,首屏未使用的包约为1MB
,仍有进一步优化的空间。
结论写在开头
在报告的开头,建议直接写上结论。大家都很忙,没那么多时间去阅读冗长的内容。结论总结了感兴趣的数据,很容易吸引读者继续阅读下去。
首屏数据报告
以下是剪映详情页的Core Web Vitals数据:
实验室数据 | 现场平均值 | TP50 | TP75 | TP90 | 建议值 | |
---|---|---|---|---|---|---|
FCP | 1.4s | 1.2s | 1s | 1.5s | 2.4s | <= 1s |
LCP | 3.5s |
3s | 2.5s | 4s | 8s | <= 2.5s |
CLS | 0.038 | 0.031 | 0.03 | 0.038 | 0.04 | <= 0.1 |
结论:LCP时间为3.5s
,仍有优化空间。
(注:我本人未曾在字节剪映工作过,上面的现场平均值和TP值都是假数据)
Core Web Vitals数据
Core Web Vitals是Google用于衡量用户页面体验的性能指标,首屏数据可重点关注下面几个指标:
● LCP:最大内容绘制。显示最大元素所需时间,首屏需要重点关注这一项指标。
● FCP:浏览器首次绘制页面的时间点。
● CLS:累积布局位移。用于衡量页面上元素在渲染过程中发生不期望的移动的情况。
● FID:首次输入延迟。衡量的是从用户首次与页面交互(例如点击链接、按钮等)到浏览器实际响应用户操作之间的延迟时间。
实验室数据与现场数据
实验室数据指在受控环境下收集的数据,最简单的实验室数据是在个人浏览器下访问页面的数据。Core Web Vitals的实验室数据可通过 Lighthouse 轻松获得。实验室数据的优点在于获取方便,且能够调节变量以获取不同情况下的数据,例如,能够自行收集带缓存和不带缓存的数据。
现场数据指真实用户环境下收集的数据。Core Web Vitals 的 Web Vitals NPM包提供了现场数据的获取途径。相比实验室数据,现场数据更全面地反映了真实情况,很多时候,实验室数据可能会忽略一些导致页面变慢的因素,而现场数据则能够将这些因素暴露出来。现场数据还有其他一些优势:确认性能优化后的效果以及制定性能优化的目标。
TP50、TP75、TP90
现场数据有多种表示形式,TP50是其中一种,代表着百分之五十的情形------也就是说,在所有观测值中,50% 的观测值小于或等于 TP50 的数值,而另外的50%的观测值大于这个数值。TP75、TP90分别是代表75%和90%的情况。
相比平均值,TP数据排除了极端数据的影响,能更真实地反映用户的实际情况。TP能够捕捉不同的百分比范围,进而更全面地展现情况。例如,TP50的数据更能代表有缓存的情况,TP90则更能代表无缓存的情况。
链路分析报告
剪映详情页的 Chrome Performance 数据如下:
抽象后的链路模型:
链路阶段的耗时如下:
流程 | 实验数据(毫秒) | 平均值(毫秒) | tp50(毫秒) | tp75(毫秒) | tp90(毫秒) |
---|---|---|---|---|---|
html加载 | 200 | 210 | 170 | 300 | 400 |
mainjs加载 | 400 | 410 | 250 | 1200 | 2000 |
其他js加载 | 500 | 550 | 300 | 1350 | 2200 |
上报资源加载 | 200 | 220 | 170 | 310 | 400 |
后台接口请求 | 2000 |
2500 | 1500 | 3400 | 5500 |
(注:我本人未曾在字节剪映工作过,上面的现场平均值和TP值都是假数据)
结论:
剪映PC详情页的性能瓶颈在于后台接口请求,大概需要2秒
。- JS 加载耗时 900毫秒,仍有可优化的空间。
- 上报资源加载需要200毫秒,非业务的资源加载不应卡住主流程。
链路分析的好处
Core Web Vitals数据可帮助识别页面性能问题,但要了解具体性能问题所在还需要进行链路分析。透过Chrome Performance工具,可以查看各项资源请求的顺序和耗时情况。将这些数据简化为链路模型能够更清晰地展示出资源耗时最长的环节,也就是性能瓶颈所在
。
最优链路模型
主要JS线程和网络线程,这两个线程能够并行工作。因此,在主要JS线程运行时,可以尽可能多地利用网络线程来请求资源,以最大限度地提高浏览器的性能。
在原有的链路模型中,当 JavaScript 执行时,网络线程是空闲的。此时可以才有数据预加载方案,即把后台请求提前并行处理,与 JavaScript 请求同时进行。通过这样的调整,之前的串行链路变为并行,从而缩短了首屏加载时间。
后台接口报告
后台接口接口梯度情况:
后台接口数据表格:
接口 | 返回数据大小(kb) | 耗时(毫秒) | 梯队 |
---|---|---|---|
tobid | 0.7 | 124 | 1 |
info/v2 | 1.6 | 217 | 1 |
webid | 0.7 | 155 | 1 |
list | 0.7 | 140 | 1 |
10585.json | 0.5 | 232 | 2 |
list | 0.7 | 156 | 2 |
get_lite_user | 1.2 | 294 | 2 |
list | 0.7 | 164 | 2 |
get_bind_account_info | 1.3 | 325 | 2 |
list | 0.7 | 171 | 2 |
get_user_group_list | 1.4 | 259 | 2 |
get_upload_token | 1.9 | 209 | 2 |
space_list | 1.1 | 154 | 3 |
price_list | 2 | 386 | 3 |
batch | 0.7 | 145 | 3 |
list | 0.7 | 120 | 3 |
price_list | 2 | 142 | 3 |
price_list | 2 | 122 | 3 |
list | 0.7 | 123 | 3 |
user_info | 1.5 | 126 | 3 |
price_list | 2 | 340 | 3 |
user_info | 1.5 | 214 | 3 |
user_info | 1.5 | 391 | 3 |
price_list | 2 | 395 | 3 |
user_info | 1.5 | 306 | 3 |
user_info | 1.5 | 395 | 3 |
user_info | 1.5 | 464 | 3 |
user_info | 1.5 | 1100 | 3 |
price_list | 2 | 1100 | 4 |
get_token | 1.1 | 1000 | 4 |
draft_permission_check | 0.8 | 1120 | 4 |
price_list | 2 | 374 | 5 |
user_info | 1.5 | 371 | 5 |
arithmet | 33 | 363 | 5 |
get_token | 1.1 | 356 | 5 |
verify | 0.6 | 137 | 5 |
batch | 0.7 | 274 | 5 |
GetUpdates | 1.2 | 109 | 5 |
GetUpdates | 1.2 | 110 | 5 |
batch | 0.7 | 104 | 5 |
list | 0.7 | 90 | 5 |
总计 | 82.4 | ||
平均 | 314.0731707 |
结论:
- 首屏后台接口共
请求了41个接口,共请求数据大小约82kb
,这么少的数据量所需的请求数过多。 - 数据串行请求,
共有5个梯度
,梯度深度过深。 - 重复使用的接口过多,如list、price_list接口。
后台接口分析的好处
如果性能瓶颈出现在后台接口请求阶段,那么就需要对后台接口请求的情况进行分析。通过分析可以得知:
- 是否存在串行请求数据的情况
- 数据请求体积是否过大
- 数据请求量是否过多
js包大小报告
剪映详情页的js包情况:
资源 | brotil传输大小(kb) | 未使用率(%) | 未使用传输大小(kb) | 原包大小(kb) | 压缩比率(%) | 涉及的npm包 | webpack-bundle-analyze |
---|---|---|---|---|---|---|---|
807.10a74351.js | 1200 | 64.6 | 775.2 | 4700 | 74.46808511 | (没有源码,获取不了) | (没有源码,获取不了) |
647.0088ca0f.js | 356 | 59.3 | 211.108 | 1500 | 76.26666667 | ||
3.9a6c2c00.js | 150 | 40.8 | 61.2 | 488 | 69.26229508 | ||
customElements.js | 23.3 | 95.5 | 22.2515 | 21.7 | -7.373271889 | ||
collect-base.js | 28.9 | 32 | 9.248 | 84.4 | 65.75829384 | ||
action.0.11.1.js | 4.1 | 35.9 | 1.4719 | 5.7 | 28.07017544 | ||
blank-screen.0.11.1.js | 3.4 | 41.3 | 1.4042 | 6.6 | 48.48484848 | ||
main.9a604005.js | 2.6 | 3.4 | 0.0884 | 4 | 35 | ||
common-monitors.0.11.1.js | 0.81 | 37.2 | 0.30132 | 18.5 | 95.62162162 | ||
总计 | 1769.11 | 1082.27332 | 6828.9 | 74.09377791 |
结论:JavaScript 传输采用了 Brotli 压缩算法,总包大小约为 1.7MB
,压缩比率为 74%。首屏未使用的包约占据 1MB
,仍有优化的潜力。通过 webpack-bundle-analyze 工具,可以查看哪些包可以进行优化(但是我没有源码,获取不了这部分数据)。
js包分析的好处
这份表格以 JavaScript 为粒度列出了资源包的大小、未使用率,以及分析所涉及的包和截图。此举旨在便于清晰地识别出那些包的体积较大且未使用率较高,并查看这些包所涉及的具体模块。通过这种方式,能够更加方便地确定哪些包可以被优化。
js包数据获取方式
js包的数据获取方式可以通过chrome的 Network和未使用率获取。
优化建议
优化建议:
- 针对后台接口请求阶段的性能瓶颈,耗时大约为2秒,可以考虑以下优化方法:
- 与后台沟通,减少或合并后台接口请求的数量。
- 将后台请求尽量由串行改为并行。
- 考虑使用 BFF(后端为前端服务)方案,以减少请求的数量。
- 考虑数据预加载的方案。
- 就 JavaScript 资源仍有1MB的优化空间而言,可以采用以下优化手段:
- 对非首屏 JavaScript 进行懒加载。
- 由于 JS 请求采用了 HTTP/2,可使用 Webpack 的 splitChunks 功能,将其拆分为更小粒度的JS,以充分利用 HTTP/2 的多路复用优势。
- 针对上报监控请求,可以考虑提前并行加载,以节省约200毫秒的加载时间。
Why Not Lighthouse?
你可能听说过性能测评工具 Lighthouse 可以分析页面的性能并生成一份性能报告。然而,需要注意的是,虽然 Lighthouse 是一种通用的分析工具,但它并不具备业务上下文,因此无法提供全面的分析。
可以看看lighthouse报告的优化建议:
如上图所示,可以发现大部分的建议都是关于减少 JavaScript 包的大小,但是这些建议并未具体指出哪些包需要进行优化。次要的建议包括减少 CSS 包的大小、资源预加载等,然而这类优化只能改善部分耗时,并非页面性能瓶颈所在。
总结
性能报告能够清晰地展示页面中性能瓶颈的具体位置,这样就可以优先处理那些性价比最高的优化项目。此外,性能报告所列举的数据能够使你的优化方案更具说服力,帮助你获取更多资源来进行优化。
另一个优点是,数据可以增强你的性能洞察力。在进行其他页面的优化时,你可以根据之前的优化数据进行对比。举例来说,若剪映页面的 JS 资源为 1.7MB,在900毫秒的耗时内加载完成,而另一个页面的JS资源为2MB,却需要使用2秒才能完成加载,这样一来,你就能识别出该页面存在问题。
最后,如果你希望在职场中获得晋升,平时积累的这些性能数据也可以成为你评审的有力支持材料,带来多重好处。
题外话
在去年3月份,我对PC端的剪映详情页性能进行了分析,那时候页面的可交互时间长达整整14秒。每次使用时都得小心翼翼,生怕刷新页面后又得忍受长时间的加载。而如今,剪映详情页的首屏可交互时间仅需3.5秒左右,耗时优化了整整75%。对剪映前端团队的能力和努力我不得不佩服。他们的优化方式着实可圈可点:
● 去掉原本的 WebAssembly 资源,改为 JS 加载,现在 JS 资源大小仅为1.7MB。
● 将 HTTP 的 Gzip 压缩方式升级为 Brotli 压缩。
● HTTP 1.1 的资源请求基本上都改为了 HTTP/2。
● 字体资源都会使用 preload 加载。
● 所有域名都使用 DNS-prefetch 预加载。
● 诸如此类的优化措施......
通过对他们页面的分析,我也从中学到了许多。