Web 性能优化完全指南:从 Core Web Vitals 到 RUM 监控

"我们的网站 LCP 是 5 秒怎么办?""老板说 PageSpeed Insights 分数太低" —— Web 性能优化是前端工程师的必修课,直接影响用户体验、转化率、SEO 排名。这篇文章把 Core Web Vitals 各个指标的优化手段讲透,涵盖从首屏加载到运行时交互的全部环节。

Core Web Vitals 三大指标

LCP(Largest Contentful Paint)

首屏最大内容元素渲染时间。目标 < 2.5s。常见 LCP 元素:首屏大图、首屏视频海报、大块文字。

INP(Interaction to Next Paint)

2024 起替代 FID。用户每次交互(点击 / 按键)到下一次渲染的延迟。目标 < 200ms。

CLS(Cumulative Layout Shift)

页面加载过程中累计的"意外位移"。目标 < 0.1。常见原因:图片没设宽高、字体切换、广告加载后插入。

LCP 优化

1. 关键资源 preload

<link rel="preload" href="/hero.webp" as="image">
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

2. 图片优化

<img src="hero.jpg" loading="eager" fetchpriority="high"
     srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1600.webp 1600w"
     sizes="(max-width: 768px) 100vw, 50vw"
     width="800" height="600"
     alt="...">

关键点:首屏图 fetchpriority="high",响应式 srcset,显式 width/height(避免 CLS)。

3. CSS 阻塞

关键 CSS 内联到 <head>,非关键 CSS 延迟。Critical / Critters 工具可以自动提取。

4. JS 阻塞

<!-- 默认 script 阻塞 HTML 解析 -->
<script src="app.js"></script>

<!-- defer:下载并行,DOM 解析完再执行 -->
<script src="app.js" defer></script>

<!-- async:下载并行,下载完立刻执行(可能打断解析) -->
<script src="analytics.js" async></script>

<!-- module 默认 defer -->
<script type="module" src="app.js"></script>

5. CDN + HTTP/2/3

静态资源走 CDN,边缘节点减少 RTT。HTTP/2 多路复用避免连接数瓶颈。HTTP/3 + QUIC 进一步降低延迟。

INP 优化

1. 减少长任务

主线程任务 > 50ms 都是"长任务"。优化:

// 长任务:在一个 tick 里干完所有事
function processItems(items) {
    items.forEach(item => expensive(item));
}

// 分片:让出主线程
async function processItems(items) {
    for (let i = 0; i < items.length; i++) {
        expensive(items[i]);
        if (i % 50 === 0) await new Promise(r => setTimeout(r, 0));
    }
}

// 用 scheduler.yield(2024 起新 API)
async function process() {
    for (const item of items) {
        expensive(item);
        if (navigator.scheduling?.isInputPending()) {
            await scheduler.yield();
        }
    }
}

2. Web Worker 后台计算

// 主线程
const worker = new Worker('worker.js');
worker.postMessage({ data: bigArray });
worker.onmessage = (e) => render(e.data);

// worker.js
onmessage = (e) => {
    const result = heavyCompute(e.data);
    postMessage(result);
};

3. requestIdleCallback

低优先级任务(日志上报、预渲染)等浏览器空闲时再做。

4. 输入响应优化

// 防抖:用户停止输入 300ms 才搜索
input.addEventListener('input', debounce(search, 300));

// 节流:滚动事件最多 100ms 一次
window.addEventListener('scroll', throttle(handleScroll, 100));

CLS 优化

1. 给图片视频设尺寸

<img src="..." width="800" height="600">
<!-- 浏览器加载前就知道占多大空间,不会插入后跳动 -->

# CSS aspect-ratio 也行
img { aspect-ratio: 4 / 3; width: 100%; height: auto; }

2. 字体优化

@font-face {
    font-family: 'Custom';
    src: url('font.woff2') format('woff2');
    font-display: swap;       /* 先显示系统字体,加载完再切 - 减少 FOIT 但有 FOUT */
    font-display: optional;   /* 加载快用,否则永远不用 - 更严格 */
}

# 预加载关键字体
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

3. 占位符

# 广告 / 嵌入内容预留固定高度
.ad-slot { min-height: 250px; }

# 图片骨架屏
.img-skeleton { background: #f0f0f0; aspect-ratio: 16/9; }

资源加载优化

1. Tree Shaking

用 ES Module 导入,打包工具(Webpack / Rollup / esbuild)能删除未用代码。

2. Code Splitting

// 路由级分割
const Settings = lazy(() => import('./Settings'));

<Suspense fallback={<Spinner />}>
    <Settings />
</Suspense>

3. 压缩

JS / CSS / HTML / SVG 都用 gzip(基础)或 brotli(更省 10-20%)。CDN 通常自动开。

4. Bundle 分析

npx webpack-bundle-analyzer dist/stats.json
# 看哪些包大,删 / 替换 / 懒加载

运行时优化

React 性能

  • React.memo:函数组件浅比较 props 是否变,没变就跳过重渲。
  • useMemo / useCallback:稳定引用,避免子组件无谓重渲。
  • 虚拟列表:react-window / TanStack Virtual。
  • 避免内联函数:每次重渲都创建新函数,触发子组件 memo 失效。

Vue 性能

  • v-once:静态内容只渲染一次。
  • v-memo:Vue 3.2+,条件性跳过更新。
  • shallowRef / shallowReactive:浅响应式,大对象别走深响应式。

监控:RUM(Real User Monitoring)

import { onCLS, onINP, onLCP, onTTFB, onFCP } from 'web-vitals';

function send(metric) {
    navigator.sendBeacon('/analytics', JSON.stringify({
        name: metric.name,
        value: metric.value,
        id: metric.id,
        delta: metric.delta,
        url: location.href,
        userAgent: navigator.userAgent,
    }));
}

onCLS(send);
onINP(send);
onLCP(send);
onTTFB(send);
onFCP(send);

实验室数据(Lighthouse)和真实用户数据(RUM)经常不一样 —— 真实用户的网络 / 设备分布广。两个都监控。Google Search Console 用 CrUX(Chrome User Experience Report)的真实数据评估 SEO。

性能预算

把性能目标写进 CI:

# lighthouse-ci 配置
{
    "ci": {
        "assert": {
            "preset": "lighthouse:no-pwa",
            "assertions": {
                "categories:performance": ["error", { "minScore": 0.9 }],
                "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
                "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }]
            }
        }
    }
}

# PR 检查,性能退步直接拦截

常见反模式

反模式 1:无限滚动 + 不清理。每滚一次加 100 条 DOM,几小时后页面有几万个 DOM 节点,卡得不行。用虚拟列表。

反模式 2:全局 store 每次都全量更新。Redux / Pinia 应该用 selector 精细订阅,而不是整个 store 都监听。

反模式 3:Console.log 在生产。大对象 console.log 性能很差,且开发者工具打开后这些日志占内存。生产构建必须 strip。

反模式 4:CSS-in-JS 过度。某些 CSS-in-JS 库每次渲染都生成新样式注入,性能糟糕。生产场景考虑 zero-runtime 方案(Vanilla Extract、Linaria)或返回普通 CSS。

写在最后

Web 性能不是一次优化完事,是持续过程。把性能监控加进 CI,把性能指标作为产品质量的一部分,你的应用才能长期保持快速。这是和"代码质量""测试覆盖率"同等重要的工程基础设施。

一图看懂

Core Web Vitals 时间轴一图看懂:

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

V8 引擎完全指南:从 Ignition 解释器到 TurboFan 优化

2026-5-15 17:32:39

技术教程

SSR / SSG / ISR 完全指南:现代渲染策略的工程选型

2026-5-15 17:38:32

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索