根据分析,在FCP阶段,页面已经加载了动画。
**注意:**相比于FCP,LCP更能体现页面性能的好坏。FCP仅仅记录内容开始加载的那一刻,但那时用户希望看到的内容并未呈现,而LCP持续更新,可以告诉我们页面的主要内容何时呈现出来。
TTI(Time to ):这个表示首次可交互可流畅的交互时间。
该指标计算略微复杂,需要满足以下三个条件:
- 从FCP指标后开始计算
- 持续5秒内无长任务(执行时间大于50ms)且无两个以上正在进行中的GET请求
- 往前回溯至5秒前的最后一个长任务结束的时间
其实这个指标和FCP一样都很重要,FCP可能意味着我们的页面开始渲染,但是渲染出页面不代表可以交互,很多时候我们的页面渲染出来了,但是会比较的卡顿
3. Speed Index:
速度指数衡量的是内容在页面加载过程中的视觉显示速度,得分越低越好。首先会在浏览器中捕获一段页面加载的视频,并计算出各帧之间的视觉进度。
TBT(Total Time):表示从FCP到TTI阶段中长任务总阻塞时间。TBT越小说明页面能够更好的快速响应用户事件。
上图表示主线程所做的任务时间线,其中任务分为长任务和短任务(长短任务的依据为执行时间是否大于50ms)
那么超过50ms的地方实际上就是阻塞时间,上面实际上的阻塞时间就是345ms。当然阻塞导致浏览器无法中断正在进行的任务,从而使得用户无法立即进行交互,从而影响体验。
5. LCP – 最大内容绘制:
LCP( Paint): LCP指标测量页面的加载,并报告在视口中可见的最大图像或文本块的渲染时间。因此,LCP会受到延迟重要信息渲染的所有因素的影响,比如说服务器响应速度慢、CSS阻塞、JS阻塞、web字体加载、耗费性能的绘制和渲染、懒加载的图片、骨架屏或者客户端渲染等等。
一个好的用户体验,LCP 应该在页面第一次开始加载的 秒内发生。这意味着我们需要尽早渲染页面的第一个可见部分。
LCP 评分低的主要原因通常是图像。如果需要在 fast 3G 上提供小于 的 LCP,基础环境是在一个优化的服务器上,所有的静态图像都不是由客服的渲染,并且图像来自于专用的 CDN,这意味着理论上的最大图像大小只有 144KB 左右,这就是为什么响应式图像很重要,以及 早期要使用 预加载关键图像
LCP目前并不会计算所有元素,因为这样会使这个指标变得非常复杂,目前LCP仅关注下面的元素:
<img>元素
<image>元素内的<svg>元素
<video>元素
通过 url() 函数加载背景图片的元素
包含文本节点或其他内联文本元素子级的块级元素。
6. CLS – 累计布局偏移:
CLS( Shift):表示页面累计布局偏移。
它测量了整个页面生命周期内发生的所有意外布局偏移分数的总和。当已经可见的元素改变其在页面上的位置时,就会发生单独的布局偏移。它的得分基于内容的大小和移动的距离。
这个指标强调了用户在访问网站时遇到意料之外的布局变化的频率,它检查不稳定元素对整体体验的影响,分数越低越好。
良好体验的推荐值是 CLS<。
每次出现改变,比如说后备字体和网页字体有不同的字体指标,或者广告、嵌入或后来加入的 ,或者图像/视频尺寸没有被保留,或者后期 CSS 强制重新绘制,后者后期 注入了更改,这些都会对 CLS 得分产生影响。
布局偏移分数计算:
布局偏移分数 = 影响分数 * 距离分数
影响分数:前一帧和当前帧的所有不稳定元素的可见区域集合(占总可视区域的部分),即影响分数。
距离分数(位移距离):任何不稳定元素在一帧中位移的最大距离(水平或垂直)除以可视区域的最大尺寸维度(宽度或高度,以较大者为准)。
上图中布局偏移分数= * =
注意,并不是所有布局移动都是不好的,很多web网站都会改变元素的开始位置,只有当布局移动是非用户预期的,才是不好的。
换句话说,当用户点击了按钮,布局进行了改动,这是ok的,CLS的JS API中有一个字段,用来标识500ms内是否有用户数据,视情况而定,可以忽略这个计算。
7. FID – 交互响应时间
First Input Delay (FID):交互响应时间,可以很好的补充 TTI
衡量 UI 的响应能力,即浏览器在对一个离散的用户输入时间(如点击)做出反应之前,忙着处理其它任务所花费的时间。它的设计目的是统计由于主线程忙碌而导致的延迟,尤其是在页面加载期间。
我们的目标是 每次交互 保持在 ms 以内
为了达到这个目标,我们需要做的是
- 识别出长任务(阻塞大于 50ms)并将其分解
- 进行
- 减少 js 执行时间
- 优化数据获取
- 延迟第三方脚本的执行
- 使用 Web 将 移动到后台线程
- 使用 减少 SPA 中的渲染代价
获取 FID 分数的可靠策略是将主线程上的任务最小化,将较大的包拆分成较小的包,并在用户需要的时候提供相应的服务,这样用户交互就不会被延迟。
8. CPU 时间消耗
显示主线程阻塞的频率和时间,用于 绘制、渲染、脚本和加载。如果用户的操作和响应之间存在明显的延迟,则可以使用 来进行测试。
9. Ad :广告占比的影响
谷歌提出了Core Web ,是用于衡量用户体验的新指标,指标将被纳入谷歌搜索引擎的网页排名。
Core Web 是用户体验和SEO的重要指标。
关键的指标包括
- LCP,用来衡量页面加载性能,为了提供良好的用户体验,LCP应该在页面首次开始加载后的秒内发生
- FID,衡量交互性能,为了提供良好的用户体验,页面的FID应该小于100毫秒。
- CLS,测量视觉稳定性。为了提供良好的用户体验,页面应该保持CLS小于。
注意: FID 和 TTI 都不考虑滚动行为; 滚动可以独立发生,因为它是非主线程的,所以对于许多内容消费网站来说,这些指标可能不那么重要
1-5 解析结果
第一次页面解析:
- LCP(最大内容绘制):
- **TTI(交互时间):**
- CLS(累积布局偏移):
- FCP:
第二次页面解析:
第三次页面解析:
第四次页面解析:
1-5-1 如何收集数据
为了收集准确的数据,我们需要仔细的选择需要测试的设备。对于大部分公司来说,这意味着研究分析并基于最常见的设备类型创建用户配置文件。然而大部分情况下,单独的分析无法提供一个完整的场景。很大一部分目标用户可能会因为体验太慢而放弃该网站并且不再返回,而他们的设备也不太可能因此成为分析中最受欢迎的设备。所以,在目标群体中去选择一个常用的设备是更好的。 我们需要考虑一个典型的测试设备应该符合:至少使用了两年、运行缓慢的3G,RTT 在 400ms 左右和约 400kb 的传输(悲观一点)(可能跟中国没有那么符合)
在现有条件下,首先测试性能最好的选择一定是公司内部的性能监控设备;其次是浏览器上的一些免费的网络审查工具。其次,测试时使用一个干净的网站配置也是十分重要的,能够排除变量。用浏览器的无痕模式进行测试就是一个较好的选择。
1-5-2 性能优化可能存在的问题如下:
- 服务器响应速度慢
- CSS阻塞
- JS阻塞
- web字体加载
- 耗费性能的绘制和渲染
- 懒加载的图片
- 骨架屏
- 客户端渲染
针对CLS:
- 后备字体和网页字体有不同的字体指标
- 广告、嵌入或后来加入的
- 图像/视频尺寸没有被保留
- 后期 CSS 强制重新绘制
- 后期 注入了更改
- 等等
1-5-3 设定现实改进目标:
1、100ms 的响应时间,60fps

了让交互感觉更加流畅,页面有 100ms 来反应用户的输入事件。超过这个时间,用户就会感觉应用是滞后的。因此,每一帧动画应该在 16ms 以内完成,从而达到 60 帧率/s(1s/60 = ms),即最好在 10ms 以内。因为浏览器需要时间在屏幕上绘制新的帧,所以你的代码应该在 之前完成执行。目前已经开始讨论 120 帧/s(比如 ipad 的屏幕是 120hz),Surma已经介绍了一些 120 帧/s 的渲染性能解决方案,但这还不是我们目前需要考虑的目标。
2、FID < 100ms, LCP < , TTI < 5s on 3G, 关键文件大小 < 170KB
虽然这可能很难实现,但一个好的最终目标是 TTI 在 5s 以下,对于重复性的访问,目标在 2s 以下。还有就是 LCP 在 以下,TBT 和 CLS 的指标尽量最小化,一个可接受的 FID 在 100ms 到 70ms(考虑的基准是 200 美元左右的安卓手机,在 3G 网络下,模拟 400msRTT 和 400kbps 的传输速度)。
二、 解决方案
图片优化(针对LCP)
webP不支持像JPEG这样的渐进渲染。使用WebP,您将减少有效负载,而使用JPEG,您将提高感知的性能
什么是呈现图像的最佳方式呢?对于插图和矢量图,(压缩的)SVG无疑是最佳选择。对于照片,我们使用图片元素的内容协商方法。如果支持AVIF,我们发送一个AVIF图像;如果不是这样,我们首先退回到WebP,如果WebP也不支持,就切换到JPEG 或 PNG 作为退回(如果需要,应用@media 条件)
(1). 响应式图片
方法一:通过 @media 实现
使用 @media 查询,你可以针对不同的媒体类型定义不同的样式,也可以针对不同的屏幕尺寸设置不同的样式。
可以在媒体查询的 CSS @media 规则 查看详情。
@media (: 769px) {
.bg {
: url(.jpg);
}
}
@media (: 768px) {
.bg {
: url();
}
}
方法二:通过 实现
在 标签中使用 来设置不同屏幕显示的图片:
<>
< =".jpg" media="(: 801px)">
< =".jpg" media="(: 800px)">
<img src=".jpg" alt="">
</>
media
对于不支持 元素的浏览器你也可以定义 元素来替代。
<>
<
sizes="(: 608px) 100vw, 608px"
="
/img/.avif 1920w,
/img/.avif 1280w,
/img/.avif 640w,
/img/.avif 320w
"
type="image/avif"
/>
<
sizes="(: 608px) 100vw, 608px"
="
/img/.webp 1920w,
/img/.webp 1280w,
/img/.webp 640w,
/img/.webp 320w
"
type="image/webp"
/>
<
sizes="(: 608px) 100vw, 608px"
="
/img/.jpg 1920w,
/img/.jpg 1280w,
/img/.jpg 640w,
/img/.jpg 320w
"
type="image/jpeg"
/>
<img src=".jpg" alt="Photo" width="450" ="350" />
</>
在设置外层容器为50%的案例中,若想图片切换是根据图片尺寸为标准,需要修改如下:
<div style="width:50%;">
<img style="width:100%;" src="img/; ="img/ 480w, img/ 800w, img/ 1600w" sizes="50vw">
</div>
在 标签中使用 来设置视频:
<video width="320" ="240" >
< src="; type="video/mp4">
< src="; type="video/ogg">
Your does not the video tag.
</video>
针对FID的优化
目标是 每次交互 保持在ms 以内。
- 识别出长任务(阻塞大于 50ms)并将其分解
- 进行
- 减少 js 执行时间
- 优化数据获取
- 延迟第三方脚本的执行
- 使用 Web 将 移动到后台线程
- 使用 减少 SPA 中的渲染代价
文本优化(针对FCP)
根据上述分析可知,页面内容的第一次绘制与对字体资源的请求之间的 ”竞争“ 导致了 ”空白文本问题“
1)如何避免显示不可见的文字
: swap@
@ {
: '';
: ;
: 400;
src: local(' '), local(''), url(https://fonts..com/s//v12/) ('woff2');
: swap;
}
swap
2)预加载网络字体
:
3)谷歌字体
&=swap
<link href="https://fonts..com/css?=:400,700&=swap" rel="">
针对FCP — 减少未使用的css
- 内联关键 CSS
- 减少样式表中未使用的规则
- 延迟未用于首屏内容的 CSS,以减少网络活动消耗的字节数。
针对CLS
1)始终在您的图像和视频元素上包含尺寸属性,或者通过使用CSS 长宽比容器之类的方式预留所需的空间。这种方法可以确保浏览器能够在加载图像期间在文档中分配正确的空间大小。
2)除非是对用户交互做出响应,否则切勿在现有内容的上方插入内容。这样能够确保发生的任何布局偏移都在预期之内。
3)首选转换动画,而不是触发布局偏移的属性动画。动画过渡的目标是提供状态与状态之间的上下文连续性。
CSS 属性使您能够在不触发布局偏移的情况下为元素设置动画:
- 用:scale()来替代和调整和width属性
- 如需使元素能够四处移动,可以用: ()来替代和调整top、right、或left属性。
减少未使用的
通过运行 的“(覆盖率)”选项卡,可以发现关键与非关键的JS文件、红色代表未利用,绿色代表利用。
图片优化
(1). 图片延迟加载
在页面中,先不给图片设置路径,只有当图片出现在浏览器的可视区域时,才去加载真正的图片,这就是延迟加载。对于图片很多的网站来说,一次性加载全部图片,会对用户体验造成很大的影响,所以需要使用图片延迟加载。
首先可以将图片这样设置,在页面不可见时图片不会加载:
<img ="https://..com/u/?s=460&u=7bc&v=4">
等页面可见时,使用 JS 加载图片:
const img = .('img')
= img..src
这样图片就加载出来了,完整的代码可以看一下参考资料。
参考资料:
(2). 响应式图片
方法一:配合sizes
之前 | 后 |
---|
标签的 src、和 sizes 属性都相互作用来实现这一最终结果。
src 属性:
属性:
sizes 属性:
在设置外层容器为50%的案例中,若想图片切换是根据图片尺寸为标准,需要修改如下:
<div style="width:50%;">
<img style="width:100%;" src="img/; ="img/ 480w, img/ 800w, img/ 1600w" sizes="50vw">
</div>
方法二:通过 实现
<>
< =".jpg" media="(: 801px)">
< =".jpg" media="(: 800px)">
<img src=".jpg" alt="">
</>
方法三:通过 @media 实现
@media (: 769px) {
.bg {
: url(.jpg);
}
}
@media (: 768px) {
.bg {
: url();
}
}
(3). 调整图片大小
例如,你有一个 1920 * 1080 大小的图片,用缩略图的方式展示给用户,并且当用户鼠标悬停在上面时才展示全图。如果用户从未真正将鼠标悬停在缩略图上,则浪费了下载图片的时间。
所以,我们可以用两张图片来实行优化。一开始,只加载缩略图,当用户悬停在图片上时,才加载大图。还有一种办法,即对大图进行延迟加载,在所有元素都加载完成后手动更改大图的 src 进行下载。
(4). 降低图片质量
例如 JPG 格式的图片,100% 的质量和 90% 质量的通常看不出来区别,尤其是用来当背景图的时候。我经常用 PS 切背景图时, 将图片切成 JPG 格式,并且将它压缩到 60% 的质量,基本上看不出来区别。
压缩方法有两种,一是通过 插件 ,二是通过在线网站进行压缩。
以下附上 插件 的用法。
npm i -D
配置
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use:[
{
: '',
: {
limit: , /* 图片大小小于1000字节限制时会自动转成 码引用*/
name: utils.('img/[name].[hash:7].[ext]')
}
},
/*对图片进行压缩*/
{
: '',
: {
: true,
}
}
]
}
(5). 尽可能利用 CSS3 效果代替图片
有很多图片使用 CSS 效果(渐变、阴影等)就能画出来,这种情况选择 CSS3 效果更好。因为代码大小通常是图片大小的几分之一甚至几十分之一。
参考资料:
(6). 调整图片大小
WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。
参考资料:
对于更复杂的图标要求,SVG 图标通常是轻量级的、易于使用的,并且可以使用 CSS 进行样式设置。与光栅图像相比,SVG 有许多优点:
- 它们是可以无限缩放的矢量图形。
- 颜色、阴影、透明度和动画等 CSS 效果很简单。
- SVG 图像可以直接内联在文档中。
- 它们是语义的。
- 它们通过适当的属性提供更好的可访问性。
善用缓存,不重复加载相同的资源
为了避免用户每次访问网站都得请求文件,我们可以通过添加 或 来控制这一行为。 设置了一个时间,只要在这个时间之前,浏览器都不会请求文件,而是直接使用缓存。而 是一个相对时间,建议使用 代替 。
不过这样会产生一个问题,当文件更新了怎么办?怎么通知浏览器重新请求文件?
可以通过更新页面中引用的资源链接地址,让浏览器主动放弃缓存,加载新资源。
具体做法是把资源地址 URL 的修改与文件内容关联起来,也就是说,只有文件内容变化,才会导致相应 URL 的变更,从而实现文件级别的精确缓存控制。什么东西与文件内容相关呢?我们会很自然的联想到利用数据摘要要算法对文件求摘要信息,摘要信息与文件内容一一对应,就有了一种可以精确到单个文件粒度的缓存控制依据了。
参考资料:
. 压缩文件
压缩文件可以减少文件下载时间,让用户体验性更好。
得益于 和 node 的发展,现在压缩文件已经非常方便了。
在 可以使用如下插件进行压缩:
- :
- CSS :
- HTML:
其实,我们还可以做得更好。那就是使用 gzip 压缩。
cnmp i -D
// 在vue..js中引入并修改配置
const = ('')
: () => {
if (.env. === '') {
// 为生产环境修改配置...
.mode = ''
{
: [new ({
test: /\.js$|\$|\.css/, //匹配文件名
: , //对超过10k的数据进行压缩
: false //是否删除原文件
})]
}
}
在服务器我们也要做相应的配置
如果发送请求的浏览器支持gzip,就发送给它gzip格式的文件
# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
1k;
# gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
5;
# 进行压缩的文件类型。有多种形式,后面的图片压缩不需要的可以自行删除
/atom+xml /geo+json / / /json /ld+json /+json /rdf+xml /rss+xml /xhtml+xml /xml font/eot font/otf font/ttf image/svg+xml text/css text/ text/plain text/xml;
# 是否在http 中添加Vary: ,建议开启
on;
# Nginx作为反向代理的时候启用
any;
# 将接收压缩文件的浏览器中排除 6,因为IE6根本不支持gzip
"msie6";
# 设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。例如 4 4k 代表以4k为单位,按照原始数据大小以4k为单位的4倍申请内存,4 8k 代表以8k为单位,按照原始数据大小以8k为单位的4倍申请内存。如果没有设置,默认值是申请跟原始数据相同大小的内存空间去存储gzip压缩结果。
16 8k;
# 识别http的协议版本。由于早期的一些浏览器或者http客户端,可能不支持gzip自解压,用户就会看到乱码,所以做一些判断还是有必要的。
;
# 设置压缩所需要的缓冲区大小
4 16k;
# 重启nginx
nginx
此外,附上 和 node 配置 gzip 的使用方法。
下载插件
npm --
npm
配置
const = ('');
. = {
: [new ()],
}
node 配置
const = ('')
// 在其他中间件前使用
(())
:false
修改vue..js中的配置
. = {
: `${}`, // 在npm run build时 生成文件的目录 type:, :'dist'
: false, // 是否在构建生产包时生成 文件,false将提高构建速度
}
如果不加false,则在打包的过程中会出现map文件,该文件的主要作用是让我们打包后的文件像未加密的代码一样,可以准确的输出相关的错误信息。默认情况下为true。设置为false后,项目打包过后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知代码具体哪里出错。
. 关闭
因为 3默认开启(预先加载模块),提前获取用户未来可能会访问的内容
在首屏会把这十几个路由文件,都一口气下载了。
**注意:**其实并不会影响首页的加载速度,只是优化子页面
使用场景:当对流量有限制时可以使用,比如移动端,只用查看首页或者其它并不是全部页面的时候,使用可能会导致流量的不必要损耗。
所以我们要关闭这个功能,在vue..js中设置
// vue..js
. = {
: => {
// 移除 插件
..('')
// 或者
// 修改它的选项:
.('').tap( => {
[0]. = [0]. || []
[0]..push(/(.)+?\.js$/)
})
}
}
. 组件按需加载
项目在打包时大家经常可以发现占用了很大一部分,因此如果在已知不会使用太多组件的情况下,可以考虑对组件进行按需加载。
使用CDN外部加载资源 vue, vuex, ,axios
一般小公司可能为了方便直接把CSS、JS、图片等文件直接传到服务器上进行访问,使用CDN的优势在于CDN是位于全球不同地方的高性能网络服务,全国的各个地方都会有服务节点,而且CDN也会缓存文件,所以通过CDN访问静态文件比直接访问服务器文件要快上几倍。
淘宝的图片访问,有98%的流量都走了CDN缓存。只有2%会回源到源站,节省了大量的服务器资源。
对于vue, vuex, ,axios等我们可以利用的参数来配置,这里我们设定只需要在生产环境中才需要使用:
// vue..js中的核心代码
const = .env. === '';
const cdn = {
css: [],
js: [
'https://cdn..com/vue/;,
'https://cdn..com//;,
'https://cdn..com/vuex/;,
'https://cdn..com/axios/;,
]
}
. = {
: => {
// 生产环境配置
if () {
// 生产环境注入cdn
.('html')
.tap(args => {
args[0].cdn = cdn;
args;
});
}
},
: => {
if () {
// 用cdn方式引入
. = {
'vue': 'Vue',
'vuex': 'Vuex',
'': '',
'axios': 'axios'
}
}
},
}
index里的相关核心代码
<!-- 使用CDN的CSS文件 -->
<% for (var i in ..cdn && ..) { %>
<link href="<%= ..[i] %>" rel="" as="style">
<link href="<%= ..[i] %>" rel="">
<% } %>
<!-- 使用CDN的JS文件 -->
<% for (var i in ..cdn && ..) { %>
<link href="<%= ..[i] %>" rel="" as="">
<% } %>
利用浏览器缓存
浏览器缓存是浏览器保存数据用于快速读取或避免请求重复资源,这有助于访客再次访问时,直接从缓存中读取内容而不必重新加载,提升了网页加载速度。有以下常用缓存方法:
1)
是HTML5的一种新的本地缓存方案,目前使用比较多,一般存储ajax返回的数据,存储特点主要有:
- 数据可以长久保存,没有有效期,直到手动删除为止。
- 存储的数据量大,一般5M以内。
- 存储的数据可以在同一个浏览器的多个窗口使用。
- 存储的数据不会发送到服务器。
常用API如下:
.(key,value) // 保存数据
.(key) // 获取数据
.(key) // 删除单个数据
.clear() // 删除全部
2)
与上述类似,它的特点主要有:
- 存储的数据在浏览器关闭后删除,与网页窗口具有相同的生命周期。
- 可以存储的数据大小5M。
- 存储的数据不会发送到服务器。
常用API如下:
.(key,value) // 保存数据
.(key) // 获取数据
.(key) // 删除单个数据
.clear() // 删除全部
3)
表示 cache对象的存储。该接口提供 或其他类型的工作线程或范围访问的所有命名缓存的主目录。
常见方法:
.match() - 检查给定的 对象是否是 对象跟踪的 Cache 对象中的键,返回
.has() - 返回一个 ,它解析为与 相匹配的 Cache 对象。
.() - 删除cache对象
.keys() - 含有keys中字符串的任意一个,则返回一个对象。
.has() - 如果包含cache对象,则返回一个对象。
4)
指的就是会话跟踪技术。一般指网站为了辨别用户身份,进行跟踪而而存储在用户本地终端上的数据,一般通过http请求头发送到服务器。主要特点有:
- 跨域限制,同一个域名下可多个网页内使用。
- 可以设置有效期,超出有效期自动清除。
- 存储大小在4K以内。
- 的存储不能超过50个。
- 只能存储字符串类型。
常用操作:
- 设置的有效期,时间单位是秒,负值时表示关闭浏览器后就失效,默认值为-1。
- 用于指定,只有请求指定域名才会带上该。
- 只有访问该域名下的的这个路径地址才会带。
- 重置 value 。
此外,还有http缓存,,,flash缓存, cache等。具体查看 前端性能优化(三)——浏览器九大缓存方法