上周发版后,Lighthouse 跑分直接给移动端首屏赏了个 35 分的红灯,LCP (Largest Contentful Paint) 指标直接飙到了 6.8 秒。点开诊断报告一看,首屏那个占了大半个屏幕的主视觉 Banner 图,居然在页面加载完毕后 5 秒才姗姗来迟。更蠢的是,因为构建打包时用了某种通用的懒加载指令,这张决定首屏生死的大图居然被硬生生套上了 loading="lazy",再加上一大堆阻塞渲染的 CSS 和字体文件在前面排队,用户不跳出才怪。
针对这种情况,首要任务就是把这张首屏大图从延迟加载的泥潭里捞出来。常规的 <img> 标签即使没有 lazyload,也会因为浏览器解析 HTML 并发现它的优先级不够高,而被推迟到网络请求队列的后端。我们需要明确地告诉浏览器:“这张图是关键渲染路径上的核心,赶紧给我抢跑。”
首先,我们在 HTML 模板的 <head> 里直接塞入 <link rel="preload"> 指令。同时,利用 HTML5 最新的 fetchpriority 属性,把图片的获取优先级直接拉满到最高阶级:
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>核心业务详情页</title>
<!-- 预加载 LCP 关键图片,并设置最高优先级 --> <link rel="preload" as="image" href="https://cdn.example.com/assets/images/hero-banner-large.webp" fetchpriority="high" type="image/webp" >
<!-- 内联 Critical CSS,避免加载外部大样式表阻塞渲染 --> <style id="critical-css"> body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background-color: #0f172a; color: #f8fafc; } .hero-container { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 80vh; background-position: center; background-size: cover; } .hero-title { font-size: 2.5rem; font-weight: 800; line-height: 1.2; margin-bottom: 1rem; background: linear-gradient(135deg, #3b82f6, #8b5cf6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .hero-banner { width: 100%; max-width: 800px; height: auto; aspect-ratio: 16 / 9; border-radius: 12px; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3); } </style>
<!-- 异步加载非关键 CSS --> <link rel="stylesheet" href="/assets/css/main.chunk.min.css" media="print" onload="this.media='all'; this.onload=null;" > <noscript> <link rel="stylesheet" href="/assets/css/main.chunk.min.css"> </noscript></head><body> <div class="hero-container"> <h1 class="hero-title">构建下一代金融级数据看板</h1> <img class="hero-banner" src="https://cdn.example.com/assets/images/hero-banner-large.webp" fetchpriority="high" alt="Hero Banner" decoding="async" > </div></body></html>光搞定图片还不够。很多前端习惯一把梭地在头部引用几百 KB 的全局 CSS 文件,导致浏览器在下载并解析完这些 CSS 之前,坚决不进行任何页面绘制(即所谓的渲染阻塞)。为了解决这个问题,我折腾了一套 Critical CSS(关键路径 CSS)提取方案。
在构建期,我们使用 PostCSS 配合 Puppeteer 模拟无头浏览器跑一遍页面,将处于首屏视口(Viewport)内的 HTML 元素所依赖 of CSS 样式强行提取出来,并直接注入到 HTML 模板的 <style> 标签中。而剩下那一大堆涉及弹窗、页脚、次要模块的非关键 CSS,则通过修改 media="print" 和 onload 回调变成异步加载,等首屏渲染完毕后再静默下载。
做完这两步改造后重新跑 Lighthouse,移动端 LCP 直接从 6.8 秒缩减到了 1.9 秒,评分终于回到了绿色的 96 分。这种细节优化不仅是对技术指标的追求,更直接关系到转化率,毕竟谁也不想在一个白屏转圈圈的网站上多等三秒钟。
你们在优化首屏时,有没有遇到过因为第三方统计脚本抢占带宽导致 preload 失效的坑?欢迎在下面聊聊解决方案。