当我们疲于开发一个接一个的需求时,很容易忘记去关注网站的性能,到了某一个节点,猛地发现,随着越来越多代码的堆积,网站变得越来越慢。
本文就是从这样的一个背景出发,着手优化网站的前端性能,并总结出一套开发习惯,让我们在日常开发时,也保持高性能,而不是又一次回过头来优化性能。
指标名称 | 优化前 | 优化后 | 提升 |
---|---|---|---|
Lighthouse Performance 评分 | 29 | 81 | 279% |
FCP(First Contentful Paint 首次内容绘制) | 0.7s | 0.7s | |
LCP(Largest Contentful Paint 最大内容绘制) | 6.2s | 2.5s | 248% |
TTI(Time to Interactive 可交互时间) | 10.1s | 2.1s | 480% |
Speed Index(速度指数) | 5.6s | 1.8 | 311% |
TBT(Total Blocking Time 总阻塞时间) | 820ms | 120ms | 683% |
优化前后对比:
接下来就是介绍下优化前我们要做哪些事件:
了解性能指标及测量工具
分析需要优化的地方
一开始我们只是感受到网站的页面打开时白屏时间较长,感觉性能是比较差的,那么具体有哪些性能指标需要去关注呢?
这里我使用的是 Chrome devtools 内置的Lighthouse,Lighthouse 是一种开源的自动化工具,用于提高 Web 应用程序的质量。
Lighthouse 会在一系列的测试下运行网页,比如不同尺寸的设备和不同的网络速度。它还会检查页面对辅助功能指南的一致性,例如颜色对比度和 ARIA 最佳实践。
打开 Chrome devtools Lighthouse 即可使用。
在比较短的时间内,Lighthouse 可以给出这样一份报告。
这份报告从 5 个方面来分析页面: 性能、辅助功能、最佳实践、搜索引擎优化和 PWA。像性能方面,会给出一些常见的耗时统计。
Performance 评分统计,包括了以下指标:
1.1.1 FCP
FCP 测量在用户导航到页面后浏览器呈现第一段 DOM 内容所花费的时间。页面上的图像、非白色<canvas>
元素和 SVG 被视为 DOM 内容;不包括 iframe 内的任何内容。
1.1.2 LCP
LCP 测量视口中最大的内容元素何时呈现到屏幕上。这近似于页面的主要内容对用户可见的时间。
需要注意的是 LCP 的计算是一个动态的过程,如下图最后的图片才是这个页面中的最大内容绘制的元素。
1.1.3 TTI
TTI 测量页面完全交互所需的时间。
TTI 是如何计算的呢,如下图首先延时间轴正向搜索时长至少为 5 秒的安静窗口(安静窗口是指没有长任务且不超过两个正在处理的网络 get 请求),然后沿时间轴反向搜索安静窗口之前的最后一个长任务,如果没有找到长任务,则在 FCP 步骤停止执行,TTI 就是安静窗口之前最后一个长任务的结束时间,如果没有找到长任务的话,则与 FCP 值相同。
1.1.4 Speed Index
Speed Index 衡量页面加载期间内容以视觉方式显示的速度。Lighthouse 首先捕获浏览器中页面加载的视频,并计算帧之间的视觉进度。
1.1.5 TBT
TBT 测量页面被阻止响应用户输入(例如鼠标点击、屏幕点击或键盘按下)的总时间。
通过添加 First Contentful Paint 和 Time to Interactive 之间所有长任务的阻塞部分来计算总和。任何执行时间超过 50 毫秒的任务都是长任务。
50 毫秒后的时间量是阻塞部分。例如,如果 Lighthouse 检测到一个 70 毫秒长的任务,则阻塞部分将为 20 毫秒。
如下图淡红色区域的时间总和就是这个页面的 TBT 分数。
用于检测 Web 应用程序整体代码健康状况,包括是否包含文档类型、图片宽高比是否正确等等。
用于检测搜索引擎对网页内容的理解程度。
了解了关键的性能指标后,就可以测量看看当前网站的性能了,
上面看到综合评分是非常低的,Lighthouse 给出了应该从哪些地方开始优化的建议。
性能优化建议主要包括以下几点:
减少未使用的 JS;
合理使用图片的格式,webp 或者 avif 更快;
延迟加载不在视图的图片;
JS 压缩;
图片的尺寸大小应该适当;
减少未使用的 CSS。
Lighthouse 诊断出的网站存在的问题:
需要加载的资源太多太大,有 147 个请求,合计 11mb;
有 40 个静态资源的缓存只有 1 小时
滚动事件没有添加标记{passive: true})
,导致需要等待侦听器完成执行后再滚动页面;
图像元素没有设置明确的宽度和高度;
JS 文件太多,主线程工作量太大、JS 执行时间太长;
最佳实践方面有以下问题:
图片的分辨率太低,清晰度不够;
没有设置 CSP 策略。
SEO 有以下问题:
没有 meta description;
图片没有 alt 属性;
robots.txt 是无效的。
根据上面 Lighthouse 报告,捋一捋项目中影响性能最大的因素,包括以下几点:
体积太大,达 11mb;
图片太大,图片格式也有影响。
检查是否还有压缩空间,或者有无工具库未压缩的。
通过 webpack-bundle-analyzer 插件分析包体积,将一些大的 npm 包和 runtimeChunk 独立分包,减小包体积。
React.lazy + Suspense 封装懒加载组件,路由级组件引入懒加载组件。
同时使用骨架屏作为懒加载的兜底组件,可以让用户感知加载更快。
在鼠标移入导航栏时预加载路由组件,可以加快页面展示。
通过 import('xx').then(xx) 按需加载工具库。
项目内有一些 json 文件存储的静态数据,这部分文件上传至 CDN,改为 fetch 的方式按需引入。
检查项目中引入的 mf、npm 资源,将没有使用到的删除。
发现业务组件库通过 npm 引入的原子组件库,而项目本身又是通过 mf 引入的原子组件库,相对于引入了 2 遍原子组件库。
这时就需要改造业务组件库,也改成用 mf 的方法引入。
因为 webpack 的按需加载是通过 import、export 来标记的,因此想要一个好的按需加载的效果,就需要避免依赖嵌套的问题。
原子组件库 mf 暴露的方式会导致只用了 1 个 icon,就会加载组件库下所有 icon 对应的 chunk,导致资源浪费。
新建一个 icon 的 npm 包用于 icon 的按需引入。
通过以上优化手段,体积从 11.7mb 降低至 1.1mb,降低 10.6 倍。
优化前:
优化后:
对非首屏的图片采用图片懒加载策略。
使用图片时,设置图片的合理尺寸。
优先使用 webp 格式图片。
优化项目内的图片分辨率。
详细的 meta description、keywords 可以加快 SEO。
<meta name="keywords" content="xx" /> <meta name="description" content="xx" />
<img src="smiley.gif" alt="Smiley face" />
再来回顾下前后对比:
优化前,明显的感知白屏时间长:
优化后,在清缓存的情况下也能实现秒开:
整体性能提升 270%:
为了在后续的迭代过程中,保持高性能,引入内部前端监控平台 -烛龙,可视化的监控前端性能。
第一步,加载 cdn 插件:
<script
defer
src="https://h5static.m.jd.com/act/jd-jssdk/latest/jd-jssdk.min.js"
></script>
第二步,在入口文件中,初始化 cdn 插件:
useEffect(() => {
// 初始化测速组件,在这里可以打开一些控制的开关,如是否上报接口
if (IS_PROD) {
// @ts-ignore
jmfe.profilerInit({
flag: xxx, // 这是应用ID,需要先在烛龙申请应用
autoReport: true,
autoAddStaticReport: true,
autoAddApiReport: true,
autoAddImageReport: false, // 支持所有图片上报,如果图片多,切记关闭,否则存在性能问题
performanceReportTime: 8000,
profilingRate: 1,
})
}
}, [])
第三步,查看监控数据:
在烛龙平台,小工具性能评分达 96分:
第四步,新增告警,实时监控
烛龙平台支持多维度的告警的服务,增加性能指标相关的告警,在性能异动时,及时发现问题,优化性能。
本文详细介绍了一个前端项目优化的详细过程,从优化前的问题分析,到具体的优化措施,最终实现了前端性能提升了近 3 倍。同时也将性能指标落到监控平台,实现可视化的监控前端性能指标。
希望能对你有所帮助,感谢阅读~
作者:京东零售 唐姣
来源:京东云开发者社区