今天,我们来深入探讨前端性能指标,重点关注 FP (First Paint - 首次绘制)、FMP (First Meaningful Paint - 首次有意义绘制)、LCP (Largest Contentful Paint - 最大内容绘制) 和 CLS (Cumulative Layout Shift - 累积布局偏移)。这些指标对于衡量网页加载体验至关重要,它们直接决定了用户是觉得你的网站“快”还是“稳定”。
我们将逐步解析如何测量、分析和应用这些指标。我们的目标是提供实用、无冗余的技术细节!
前端性能直接影响用户体验。加载缓慢或内容跳跃的页面会赶走用户。谷歌的 核心网页指标 (Core Web Vitals) 将 LCP、CLS 和 FID (First Input Delay - 首次输入延迟,本文不深入探讨) 作为关键指标,它们甚至会影响搜索引擎排名。
虽然 FP 和 FMP 已不是官方核心指标,但它们在分析渲染过程中仍然至关重要。让我们从 FP 开始,逐一揭开这些指标的神秘面纱。
FP (First Paint) 标志着浏览器在屏幕上绘制第一个像素的时刻。这个像素可以是背景颜色、边框或任何视觉元素——不一定是用户关心的“主要内容”。FP 是一个基础指标,它表明页面从白屏到开始显示内容花费了多长时间。
FP 是第一个渲染里程碑,通常通过 Performance api 来捕获。它发生在浏览器解析完 dom 和 cssOM 并开始绘制像素时。以下是测量方法:
// 监听页面加载完成事件
window.addEventListener("load", () => {
// 使用 Performance API 获取所有“paint”类型的性能条目
const performanceEntries = window.performance.getEntriesByType("paint");
// 找到名为 'first-paint' 的条目
const fpEntry = performanceEntries.find((entry) => entry.name === "first-paint");
if (fpEntry) {
// fpEntry.startTime 是从页面导航开始到首次绘制发生的时间(毫秒)
console.log(`首次绘制 (FP): ${fpEntry.startTime.toFixed(2)}ms`);
} else {
console.log("浏览器不支持 FP 或无法获取");
}
});
这段代码使用 performance.getEntriesByType('paint') 来获取与绘制相关的性能条目,找到 first-paint 条目并记录其 startTime (以毫秒为单位)。大多数现代浏览器(Chrome、Edge、Firefox)都支持此 API。
FP 标志着渲染的开始,表明浏览器已经开始工作。例如,背景颜色的变化或加载指示器的出现都会触发 FP。它通常发生在:
思考一个简单的页面:
<!DOCTYPE html>
<html>
<head>
<title>我的页面</title>
<link
rel="stylesheet"
href="styles.css"
/>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
/* styles.css */
body {
background-color: #f0f0f0; /* 设置背景色 */
}
h1 {
color: navy;
}
当浏览器加载 styles.css 并应用 body 的背景色时,屏幕从白色变为 #f0f0f0,这便触发了 FP。此时 <h1> 文本可能尚未出现,因为 DOM 和 CSSOM 可能仍在构建中。
FP 只追踪第一个像素的绘制,而不管内容是否有意义。例如,即使用户想看的文章或图片还未加载,一个背景色的变化也会触发 FP。这正是 FMP 和 LCP 发挥作用的地方。
注意: FMP 是一个已被 Lighthouse 废弃的指标,因为它定义模糊且难以标准化。现在官方推荐使用 LCP。但了解 FMP 有助于理解性能演进历史。
FMP (First Meaningful Paint) 捕获页面主要内容(如文章标题、主图或导航栏)首次渲染的时刻。它不是一个标准化的浏览器指标,定义可能很模糊,但像 Lighthouse 这样的工具曾用它来分析加载体验。
FMP 无法通过浏览器 API 直接获得,因为“有意义”的内容因页面而异。Lighthouse 通过分析渲染时间线,寻找“视觉上显著”的元素(如大块文本或图片)出现的时间点来估算 FMP。
以下是如何在 Node.js 中使用 Lighthouse 测量 FMP:
// 引入 lighthouse 和 chrome-launcher 库
const lighthouse = require("lighthouse");
const chromeLauncher = require("chrome-launcher");
asyncfunction runLighthouse(url) {
// 启动一个无头 Chrome 实例
const chrome = await chromeLauncher.launch({ chromeFlags: ["--headless"] });
const options = {
output: "json", // 输出格式为 JSON
onlyCategories: ["performance"], // 只运行性能审计
};
// 运行 Lighthouse 分析
const runnerResult = await lighthouse(url, options, chrome);
// 从报告中获取 FMP 的估算值(毫秒)
console.log("首次有意义绘制 (FMP):", runnerResult.lhr.audits["first-meaningful-paint"].numericValue, "ms");
// 关闭 Chrome 实例
await chrome.kill();
}
// 运行分析
runLighthouse("http://localhost:3000");
首先,需要安装依赖:
npm install lighthouse chrome-launcher
这段代码运行 Lighthouse,分析页面,并返回估算的 FMP 时间。Lighthouse 通过观察 DOM 变化和渲染事件来推断 FMP。
FMP 的定义因页面而异,Lighthouse 的估算也未必总是准确。例如:
这促使我们转向 LCP,这是 Google 提出的官方指标,旨在解决 FMP 的模糊性问题。
LCP (Largest Contentful Paint) 是核心网页指标之一,用于测量页面上最大的内容元素(如图片、视频或文本块)完全渲染出来所需的时间。它比 FMP 更精确,因为它只关注视口内最大的可见元素。
LCP 可以通过 Performance API 的 LargestContentfulPaint 条目捕获。浏览器会持续追踪视口中最大的内容元素,并记录其渲染时间。
// 创建一个 PerformanceObserver 实例来监听性能事件
const observer = new PerformanceObserver((entryList) => {
// getEntries() 返回一个性能条目数组
const entries = entryList.getEntries();
for (const entry of entries) {
// entry.startTime 是 LCP 时间
console.log(`最大内容绘制 (LCP): ${entry.startTime.toFixed(2)}ms`);
// entry.element 是触发 LCP 的 DOM 元素
console.log("LCP 元素:", entry.element);
}
});
// 开始监听 'largest-contentful-paint' 类型的事件
// buffered: true 确保在观察者创建之前发生的事件也能被捕获
observer.observe({ type: "largest-contentful-paint", buffered: true });
你也可以在 Chrome DevTools 的 Performance 面板中查看 LCP。打开 DevTools (F12),转到 "Performance" 标签页,录制页面加载过程,LCP 会在时间线上被明确标记出来。
LCP 追踪的元素包括:
浏览器会:
这是一个突出 LCP 的页面:
<!DOCTYPE html>
<html>
<head>
<title>LCP 演示</title>
<style>
.hero {
width: 100%;
height: 400px;
/* 一个大的背景图通常是 LCP 元素 */
background: url("hero.jpg") no-repeat center/cover;
}
h1 {
font-size: 48px;
}
</style>
</head>
<body>
<p class="hero"></p>
<h1>欢迎来到我的网站</h1>
<p>一些内容...</p>
</body>
</html>
假设 hero.jpg 是一张大图(例如 1920x1080),渲染顺序可能是:
CLS (Cumulative Layout Shift) 衡量页面内容在加载过程中“跳动”或“偏移”的程度。用户最讨厌的就是,正要点击一个按钮,页面突然移动,导致误点。CLS 是核心网页指标之一,旨在确保页面稳定性。
CLS 是通过 LayoutShift 性能条目计算的。浏览器会追踪所有非用户交互触发的布局变化,并计算一个“偏移分数”。
let clsScore = 0;
// 创建 PerformanceObserver 来监听布局偏移事件
const observer = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// 忽略由用户输入(如点击、滚动)在 500ms 内触发的布局偏移
if (!entry.hadRecentInput) {
// entry.value 是单次布局偏移的分数
clsScore += entry.value;
console.log(`单次布局偏移分数: ${entry.value.toFixed(4)}`);
}
}
// 累加所有分数得到最终的 CLS
console.log(`累积布局偏移 (CLS): ${clsScore.toFixed(4)}`);
});
// 开始监听 'layout-shift' 类型的事件
observer.observe({ type: "layout-shift", buffered: true });
根据 Google 的标准,CLS 分数低于 0.1 是理想的。你可以使用 Chrome DevTools 的 Performance 面板来可视化 CLS,发生偏移的元素会被高亮显示。
CLS 是所有单个布局偏移分数的总和,每个分数按以下方式计算:
偏移分数 = 影响因子 (Impact Fraction) × 距离因子 (Distance Fraction)
这是一个会导致 CLS 的页面:
<!DOCTYPE html>
<html>
<head>
<title>CLS 演示</title>
<style>
.banner {
width: 100%;
height: 100px;
background: lightblue;
}
.content {
margin-top: 10px;
}
</style>
</head>
<body>
<p class="content">
<h1>主要内容</h1>
<p>这部分内容可能会移动...</p>
</p>
<script>
// 模拟异步加载广告
setTimeout(() => {
const banner = document.createElement("p");
banner.className = "banner";
// 将广告横幅插入到 body 的最前面,导致下面的内容移动
document.body.insertBefore(banner, document.body.firstChild);
}, 2000);
</script>
</body>
</html>
这个页面:
为图片设置尺寸:给 <img> 标签添加 width 和 height 属性,以便浏览器提前预留空间。
<img
src="image.jpg"
width="800"
height="600"
alt="示例图片"
/>
使用 CSS 占位符:为即将加载的动态内容预留空间。
.banner-placeholder {
width: 100%;
min-height: 100px; /* 预留空间 */
}
避免在现有内容上方插入新内容。
让我们分析一个包含 FP、FMP、LCP 和 CLS 的真实页面:
<!DOCTYPE html>
<html>
<head>
<title>新闻网站</title>
<link
rel="stylesheet"
href="styles.css"
/>
<!-- 加载外部字体 -->
<link
href="https://fonts.googleapis.com/css2?family=Roboto"
rel="stylesheet"
/>
</head>
<body>
<header>
<h1>新闻网站</h1>
</header>
<main>
<!-- LCP 候选元素 -->
<img
src="hero.jpg"
alt="主图"
/>
<h2>头条新闻</h2>
<p>突发新闻内容...</p>
</main>
<!-- 异步加载的 JS 可能导致 CLS -->
<script
src="ads.js"
async
></script>
</body>
</html>
/* styles.css */
body {
background-color: #f0f0f0;
}
header {
background: navy;
color: white;
padding: 10px;
}
/* 图片未设置高度,可能导致 CLS */
img {
width: 100%;
height: auto;
}
main {
margin: 20px;
}
h2,
p {
font-family: "Roboto", sans-serif;
}
// ads.js
// 模拟一个广告脚本,在 3 秒后插入一个横幅
setTimeout(() => {
const ad = document.createElement("p");
ad.style.height = "100px";
ad.style.background = "lightgreen";
document.body.insertBefore(ad, document.body.firstChild);
}, 3000);
使用 React/Vue 等框架的 SPA 由于其动态渲染机制,使性能指标分析变得复杂。
// src/App.js
import React, { useEffect, useState } from"react";
import"./App.css";
function App() {
const [adVisible, setAdVisible] = useState(false);
useEffect(() => {
// 模拟 3 秒后显示广告
const timer = setTimeout(() => {
setAdVisible(true);
}, 3000);
return() => clearTimeout(timer);
}, []);
return (
<p>
{/* 广告位,会导致布局偏移 */}
{adVisible && <p className="ad-banner">广告</p>}
<header>
<h1>React 新闻</h1>
</header>
<main>
<img
src="hero.jpg"
alt="主图"
style={{ width: "100%" }}
/>
<h2>头条新闻</h2>
<p>突发新闻内容...</p>
</main>
</p>
);
}
exportdefault App;
JS 阻塞:大型 JS 包会因为解析和执行而延迟 FMP 和 LCP。
动态渲染: 通过 useEffect 或异步请求加载的内容会延迟 LCP。
CLS 风险: 动态组件(如广告、评论)会增加布局偏移风险。
// 使用占位符修复 CLS
.ad-placeholder {
min-height: 100px;
}
{/* 在广告加载前显示占位符 */}
{adVisible ? <p className="ad-banner">广告</p> : <p className="ad-placeholder"></p>}
Chrome DevTools: Performance 面板可以直观地显示 FP、LCP 和 CLS。
Lighthouse: 运行全面的性能审计,提供 FMP、LCP 等指标的得分和建议。
web-vitals 库: Google 官方的轻量级库,用于在真实用户环境中监控 LCP 和 CLS。
npm install web-vitals
import { getLCP, getCLS } from "web-vitals";
// 监控 LCP
getLCP((metric) => console.log("LCP:", metric.value.toFixed(2), "ms"));
// 监控 CLS
getCLS((metric) => console.log("CLS:", metric.value.toFixed(4)));
FP、FMP、LCP 和 CLS 是衡量前端性能的关键指标:
通过使用 Performance API、Lighthouse 和 web-vitals 库,您可以准确地测量这些指标。本文中的代码示例演示了如何进行监控和分析,并通过真实世界的案例(静态页面、SPA、复杂页面)阐明了每个指标的触发条件和影响。
现在,就动手试试这些脚本,打开 DevTools,享受性能分析的乐趣吧!
来自公众号:OTT前端技术
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
为解决JS加载速度慢,采用js的延时加载,和动态加载。由于js的堵塞特性,当浏览器在加载javascript代码时,不能同时做其他任何事情,如果javascript执行时间越久,浏览器等待响应的时间就越久。
如何提高CSS性能,根据页面的加载性能和CSS代码性能,主要表现为: 加载性能 (主要是从减少文件体积,减少阻塞加载,提高并发方面入手),选择器性能,渲染性能,可维护性。
css的加载是不会阻塞DOM的解析,但是会阻塞DOM的渲染,会阻塞link后面js语句的执行。这是由于浏览器为了防止html页面的重复渲染而降低性能,所以浏览器只会在加载的时候去解析dom树,然后等在css加载完成之后才进行dom的渲染以及执行后面的js语句。
性能十分重要。然而,我们真的知道性能瓶颈具体在哪儿吗?是执行复杂的 JavaScript,下载缓慢的 Web 字体,巨大的图片,还是卡顿的渲染?研究摇树(Tree Shaking),作用域提升(Scope Hoisting)
Js高性能总结:加载和运行、数据访问、DOM编程、算法和流程控制、响应接口、Ajax 异步JavaScript和XML、编程实践...
前端网站性能优化规则:网络加载类、页面渲染类。包括:减少 HTTP 资源请求次数、减小 HTTP 请求大小、避免页面中空的 href 和 src、合理设置 Etag 和 Last-Modified、使用可缓存的 AJAX、减少 DOM 元素数量和深度等
性能一直以来是前端开发中非常重要的话题。随着前端能做的事情越来越多,浏览器能力被无限放大和利用:从 web 游戏到复杂单页面应用,从 NodeJS 服务到 web VR/AR 和数据可视化,前端工程师总是在突破极限
BigPipe是一个重新设计的基础动态网页服务体系。大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器和浏览器建立管道并管理他们在不同阶段的运行。这是类似于大多数现代微处理器的流水线执行过程:多重指令管线通过不同的处理器执行单元,以达到性能的最佳。
你知道我们可以在浏览器中用css开启硬件加速,使GPU (Graphics Processing Unit) 发挥功能,从而提升性能吗?现在大多数电脑的显卡都支持硬件加速。鉴于此,我们可以发挥GPU的力量,从而使我们的网站或应用表现的更为流畅。
像淘宝网站等,页面中有着大量图片,一次性全部加载这些图片会使浏览器发送大量请求和造成浪费。采用懒加载技术,即用户浏览到哪儿,就加载该处的图片。这样节省网络资源、提升用户体验、减少服务器压力。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!