什么是 FCP
FCP(First Contentful Paint,首次内容绘制)是指浏览器从开始加载页面,到第一个内容元素出现在屏幕上所经过的时间。
"内容元素"包括:
文本、图片(含背景图)、SVG、canvas
不包括:
空白的 div、loading spinner 的纯白背景
FCP 评分标准
Google 给出的标准:
| 时间 | 评级 |
|---|---|
| 0 ~ 1.8s | 🟢 良好 |
| 1.8s ~ 3s | 🟡 需要改善 |
| 3s 以上 | 🔴 较差 |
FCP 和其他指标的区别
FP (First Paint) 首次绘制,哪怕只画了一个像素
FCP (First Contentful Paint) 首次有意义内容出现 ← 本题
LCP (Largest Contentful Paint) 最大内容元素出现
TTI (Time to Interactive) 页面可交互
时间轴:
|---FP---|---FCP---|--------LCP--------|---TTI---|
有像素 有内容 主要内容加载完 可交互
FCP 关注的是用户感知到"页面开始有东西了" 的那一刻,比 LCP 更早,是用户体验的第一道关卡。
如何测量 FCP
方式一:Chrome DevTools(开发阶段最常用)
打开 DevTools → Performance 面板 → 录制页面加载
录制后能看到时间轴,FCP 会用绿色竖线标出
或者 Lighthouse 面板,直接生成报告,列出 FCP 具体数值和优化建议。
方式二:Performance API(代码测量,线上监控必用)
// 用 PerformanceObserver 监听 FCP
const observer = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (entry.name === 'first-contentful-paint') {
console.log('FCP:', entry.startTime, 'ms')
// 上报到监控系统
reportToAnalytics({ metric: 'FCP', value: entry.startTime })
observer.disconnect()
}
}
})
observer.observe({ type: 'paint', buffered: true })
buffered: true 很重要,防止脚本加载慢导致错过 FCP 事件。
方式三:web-vitals 库(推荐,最简单)
Google 官方出的库,封装好了所有核心指标:
import { onFCP, onLCP, onCLS, onTTFB } from 'web-vitals'
onFCP((metric) => {
console.log('FCP:', metric.value, 'ms')
// 上报
sendToServer({ name: metric.name, value: metric.value })
})
一行搞定,比手写 PerformanceObserver 简洁很多。
方式四:pagespeed网站
线上真实用户数据(Field Data),输入网址直接分析:
https://pagespeed.web.dev/
这里的数据来自真实用户,比 DevTools 模拟更有参考价值。
FCP 慢的常见原因和优化
原因一:渲染阻塞资源
<!-- ❌ CSS 和 JS 都会阻塞渲染 -->
<head>
<link rel="stylesheet" href="huge-style.css">
<script src="huge-bundle.js"></script>
</head>
<!-- ✅ JS 加 defer/async,CSS 只保留关键部分 -->
<head>
<style>/* 内联关键 CSS */</style>
<script src="bundle.js" defer></script>
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">
</head>
原因二:服务器响应慢(TTFB 高)
TTFB(首字节时间)高 → FCP 一定高
优化方向:CDN、服务端缓存、接口提速
原因三:资源体积太大
优化方向:
- JS/CSS 压缩、Tree Shaking
- 图片压缩、转 WebP 格式
- 开启 Gzip / Brotli 压缩
- 路由懒加载,减少首屏 JS 体积
原因四:字体加载阻塞文字显示
/* ❌ 默认等字体加载完才显示文字 */
font-display: block;
/* ✅ 先用系统字体显示,字体加载完后替换 */
font-display: swap;
原因五:没有服务端渲染
CSR(纯前端渲染):
浏览器下载 HTML → 下载 JS → 执行 JS → 渲染内容
FCP 很晚,因为要等 JS 执行完
SSR(服务端渲染):
服务器直接返回带内容的 HTML → 浏览器直接渲染
FCP 很早,因为 HTML 里已经有内容了
面试加分点
FCP 和 LCP 哪个更重要?
都重要但关注点不同。FCP 反映用户"看到东西"的速度,LCP 反映"看到主要内容"的速度。LCP 和用户实际体验更相关,是 Google Core Web Vitals 的核心指标之一,直接影响 SEO 排名。FCP 是 LCP 的前置条件,FCP 慢 LCP 一定慢。
线上监控 FCP 要注意什么?
要区分 P50(中位数)和 P75、P90 等百分位数。Google 建议看 P75,即 75% 的用户体验到的 FCP 值,而不是平均值,因为平均值会被少数极快的请求拉低,掩盖大部分用户的真实体验。
FCP 为 0 或异常小是什么原因?
可能是页面有预渲染或者 Service Worker 缓存,浏览器直接从缓存返回了内容,导致 FCP 极小,这是正常的优化效果,不是 bug。