前端性能指标
FP
First Paint
首次绘制(反映了页面的白屏时间 – 从用户开始访问 Web 页面的时间点到FP的时间点 )
浏览器首次将像素渲染到屏幕上的时间点
FCP
First Contentful Paint
首次内容绘制(用户开始与页面进行视觉交互的速度)
页面绘制其第一个非白色元素(如文本、图像、非空白 canvas 或 SVG)所需的时间。
优化目标:1.8 秒内
优化思路:
- 静态资源部署到 CDN
- 移除无效资源(如多余的 css 和 js 等)
- 延迟加载非关键资源(使用 defer / async 延迟加载 js)
- 降低服务器响应时间
- 内联关键样式(慎用,不方便维护)
DCL
DOMContentLoaded 事件
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。
LCP
Largest Contentful Paint
最大内容绘制
视窗内最大的元素绘制的时间,会随着页面渲染变化而变化,因为页面中的最大元素在渲染过程中可能会发生改变,会在用户第一次交互后停止记录。
优化目标:2.5 秒内
优化思路:
重点关注以下元素的加载和渲染:
<img>
元素。<svg>
元素内嵌的<image>
元素。<video>
元素。- 使用
url()
函数加载背景图片的元素。 - 包含文本节点或其他内嵌文本元素子元素的块级元素。
L
Load 事件
load 事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。它与 DOMContentLoaded不同,后者只要页面 DOM 加载完成就触发,无需等待依赖资源的加载。
- 若无外部资源,load 事件可能比DOMContentLoaded 事件先触发
- 有很多外部资源需要加载时,DOMContentLoaded 事件先触发
其他
TTFB
Time to First Byte
首字节时间
从点击网页到接收到第一个字节的时间
优化目标:800 毫秒内
优化思路:
优化以下时间:
- 重定向时间
- Service Worker 启动时间
- DNS 查找
- 连接和 TLS 协商
- 请求,直到响应的第一个字节到达
TBT
Total Blocking Time
总阻塞时间
从首次内容绘制(FCP)到页面达到可交互时间(TTI)期间,主线程因运行长任务(执行时间超过 50 毫秒的 JavaScript 任务)而被阻塞的总时间
优化目标:移动端 300 毫秒内, Web 端 100 毫秒内
优化思路:
- 减轻第三方代码的影响
- 缩短JavaScript执行时间
- 减少主线程的工作量
- 控制请求数量和传输大小
FMP
First Meaningful Paint
首次有效渲染
从用户开始加载页面到浏览器首次渲染出对用户来说有意义的内容(如文本、图片、按钮等可交互元素)所花费的时间。
因有意义需根据具体业务确定,已弃用,改用 LCP
SI
Speed Index
速度指数( 用于衡量页面渲染用户可见内容迅速程度 )
页面从加载开始到页面内容基本可见的过程中,用户感受到的加载速度。
优化目标:移动端 3.4 秒内, Web 端 1.3 秒内
CLS
Cumulative Layout Shift
累积布局偏移
页面中可见元素在加载过程中由于内容加载而发生的位置偏移
优化目标:0.1 内
影响 CLS 分数的常见原因
- 图片、视频和 iframe 没有预先设定尺寸,导致页面加载时元素位置变化。
- 字体加载过程中可能产生文本短暂消失或样式未加载的文本闪烁,影响布局稳定性。
- 动态内容(如广告、通知等)注入 DOM 后,尤其是网络请求之后,往往会导致页面布局发生突变。
编写 HTML 和 CSS 时的注意事项
- 不要将新元素插入到现有元素之上
- 为图像和视频指定尺寸
- 选择那些不会移动元素或改变布局尺寸的过渡效果
INP
Interaction to Next Paint
交互到下一次绘制
用户与网页交互后,浏览器完成下一次屏幕绘制所需的时间
INP 会测量以下延迟:
- 输入延迟:用户交互和浏览器能够处理事件之间的时间,类似于 FID。
- 处理延迟:浏览器处理事件处理程序所需的时间
- 显示延迟:浏览器重新计算布局并将像素绘制到屏幕上所需的时间。
优化目标:200 毫秒内
FID
First Input Delay
首次输入延迟
用户首次点击按钮、链接、输入字段等可交互元素时,页面响应这些交互所需的时间。
FID 仅测量了页面上首次互动的输入延迟,而 INP 则通过考虑所有页面互动(从输入延迟到运行事件处理程序所需的时间,再到浏览器绘制下一帧)来改进FID。这使得 INP 成为更可靠的整体响应能力指标。
TTI
Time To Interactive
可交互时间
页面从开始加载到用户可以顺畅地与之交互的时间点
TTI 时间主要取决于以下因素:
- 页面布局稳定,所有可见的内容都已经被加载。
- 主线程空闲。如果还在加载脚本或处理其他任务,那么应用将不会处于交互状态。用户的点击和其他操作将被忽略(或排队)直到线程空闲。
优化思路:
- 清除冗余脚本
- 分割脚本文件:将大型脚本拆分成多个较小的文件。这有助于浏览器更有效地加载和解析这些脚本,减少阻塞时间。
- 动态加载脚本:对于来自外部资源且无法直接分割或修改的脚本,考虑采用动态加载的方式,以减少对页面初始加载时间的影响。
前端性能监测
方式 1:Chrome DevTools
在性能选项卡中,对页面加载过程进行录制
方式 2:linghtHouse
安装
npm i lighthouse -g
使用
lighthouse 目标网址 --view --preset=desktop
范例
lighthouse https://blog.csdn.net/weixin_41192489 --view --preset=desktop
最终结果
还有很多优化建议:
前端性能问题排查
- 借助性能监测工具,分析性能指标
- 使用“二分法”,逐步找出问题根源
- 对症下药,解决问题
- 持续跟进,持续优化
前端性能优化方向
- 减少网络加载耗时
- 减少 CPU 计算量
都可以考虑用空间换时间
性能优化是一个循序渐进的过程,需持续跟进统计结果,再逐步分析性能瓶颈,持续优化,可使用第三方统计服务,如阿里云 ARMS、百度统计
让加载更快
减少资源体积
清理冗余的代码、图片和视频等无效资源
优化代码结构
- 将重复的代码提炼为公共组件、公共样式、全局变量和函数等。
- 路由懒加载,大组件异步加载 - 减少主包的体积
打包构建时,将代码和资源进行压缩
减少访问服务器的次数
合并代码,减少代码文件数量
使用 SSR 服务器渲染
将网页和数据一起加载,一起渲染,如早先的 JSP ASP PHP,现在的 vue React SSR
非 SSR(前后端分离) : 先加载网页,再加载数据,再渲染数据
- 尽可能多地使用浏览器的缓存
以 webpack 打包构建的 hash 命名机制为例:- 静态资源加 hash 后缀,根据文件内容计算 hash
- 文件内容不变,则 hash 不变,则 ur 不变
- ur 和文件不变,则会自动触发 http 缓存机制,返回 304
优化 HTTP 缓存策略
使用更快的网络
使用 CDN 加载静态资源(通用的 js ,css 文件,图片,视频等)
让渲染更快
CSS 放在 head 里
<head>
<meta charset="UTF-8">
<title>网页标题</title>
<!-- 引入外部 CSS 文件 -->
<link rel="stylesheet" href="styles.css">
<!-- 内联 CSS 样式 -->
<style>
body {
background-color: #f1f1f1;
font-family: Arial, sans-serif;
}
h1 {
color: blue;
}
</style>
</head>
Js 放在 body 最下面
<body>
<!-- 此处省略了其他HTML内容 -->
<!-- 将JavaScript代码放置在body标签的最后部分 -->
<script src="your_js_file.js"></script>
</body>
尽早执行JS,用DOMContentLoaded 触发
window.addEventListener("load", function () {
// 页面的全部资源加载完才会执行,包括图片视频等
});
document.addEventListener("DOMContentLoaded", function () {
// DOM 渲染完即可执行,此时图片、视频可能还没有加载完
});
使用懒加载
监听页面的滚动,当资源(如图片/视频)即将进入视口(用户可见的网页区域)时,再加载资源。
具体实现方法可参考
缓存DOM 查询
频繁的DOM 操作,合并到一起插入 DOM 结构
防抖 debounce
等待高频事件结束时才触发操作(关注【结果】)
功效:降低了高频事件内操作的频率,避免了性能的浪费,有效降低页面卡顿的风险。
场景:
- 监听用户输入,实时搜索(如百度搜索),用防抖来等待用户停止输入时,执行一次查询操作。
- 不断地调整浏览器窗口大小会不断的触发 window 的 resize 事件,用防抖来等待用户停止调整浏览器窗口大小时,执行一次 resize 事件内的操作。
原理:见下图
代码 : 需能手写
// 防抖
function debounce(fn, delay = 500) {
// timer 是闭包中的
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
使用
const input1 = document.getElementById('input1')
input1.addEventListener('keyup', debounce(function (e) {
console.log(input1.value)
}, 600))
实战 : 使用 lodash
节流 throttle
让高频事件内的操作,按预期的频率触发(关注【过程】)
功效:降低了高频事件内操作的频率,避免了性能的浪费,有效降低页面卡顿的风险。
场景:
- 拖拽元素时,避免 drag 事件内的操作过于频繁的执行
- 滚动过程中,避免滚动事件内的操作过于频繁的执行
原理:见下图
代码 : 需能手写
// 节流
function throttle(fn, delay = 100) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
使用
const div1 = document.getElementById('div1')
div1.addEventListener('drag', throttle(function (e) {
console.log(e.offsetX, e.offsetY)
}))
实战 : 使用 lodash
选择性能更优的动画渲染方案
-
需实时更新的动画,使用 RAF
-
简单的静态或者中等复杂度的动画,用 CSS
-
若使用的 setTimeout 实现的动画,建议改用 CSS 或 RAF 进行优化!