返回博客列表

性能优化

独立站首屏 8 秒变 2 秒:我做了这 4 件事

2026-07-08

我的 Shopify 独立站首屏加载要 8.4 秒,PageSpeed 移动端得分 31 分。

用户还没看到商品,就已经等烦了。数据也印证这一点:加载超过 3 秒,有 53% 的用户会直接离开;加载时间从 5 秒降到 1 秒,转化率平均提升 28%。每一秒都在影响收入。

做完这 4 项优化之后,首屏降到了 1.9 秒,PageSpeed 移动端分数升到 89 分。这篇文章按性价比从高到低,完整拆解每一步做了什么、为什么这样做。

优化前:8.4 秒 · PageSpeed 31 分 🔴 图片压缩后:4.1 秒 🟡 全部优化后:1.9 秒 · PageSpeed 89 分 🟢


先做诊断:用 PageSpeed 找到瓶颈

优化之前先测,不知道问题在哪里就无从下手。

打开 pagespeed.web.dev,输入你的网址,选 Mobile(移动端),得到报告。重点看三个指标:

LCP(最大内容渲染时间):首屏最大元素(通常是 Banner 图或商品主图)加载完成的时间。这是最重要的单个指标,Google 的标准是 2.5 秒以内为良好,超过 4 秒为差。我的初始值是 7.2 秒——红色。

TBT(总阻塞时间):JS 脚本占用主线程,导致用户点击没有响应的感知时长。超过 200ms 就开始影响交互体验,超过 600ms 用户会明显感到卡顿。我的初始值是 840ms——第三方 App 脚本是主要来源。

CLS(累计布局偏移):页面加载过程中元素跳动的幅度。超过 0.1 用户就会察觉,0.18 已经很明显。最常见的原因是图片没有设置 widthheight 属性,浏览器不知道预留多少空间。

看完这三个数字,基本就能判断优化重点在哪里。我的情况是 LCP 和 TBT 都很差,意味着图片和 JS 脚本是主要问题,和下面的四项优化完全对应。


优化 01 · 图片格式和懒加载(单项提升最大)

贡献:图片体积减少 65%,LCP 直接从 7.2s 降到约 3.8s

图片是独立站首屏最大的性能杀手,也是单项优化里收益最高的。同样内容、同样质量的图片,不同格式的体积差距极大:

格式体积说明
JPEG420 KB多数店铺直接上传原图
PNG340 KB透明图用 PNG,体积更大
WebP145 KB同等画质,体积减少 65%
AVIF105 KB更新格式,压缩率最高,兼容性稍差

三个具体操作

① Shopify 2.0 主题开启 WebP 自动转换

在 Liquid 模板里用 image_tag 替换原有的 img 标签写法,加上 format: 'webp' 参数,Shopify CDN 会自动给支持 WebP 的浏览器提供 WebP 版本,旧浏览器自动回退 JPEG:

{{ product.featured_image
   | image_url: width: 800
   | image_tag:
       loading: 'lazy',
       widths: '400, 600, 800',
       sizes: '(max-width: 768px) 100vw, 50vw' }}

② 按展示尺寸设 sizes 属性,不要传超大原图

Banner 区最大显示宽度 1440px,商品卡片最大 600px。浏览器会根据 sizes 属性和设备像素比选择合适尺寸下载,不会把 3000px 的原图传到 375px 宽的手机屏幕上——超出展示尺寸的像素全是浪费带宽。

③ 首屏图加 fetchpriority="high",其余加 loading="lazy"

<!-- Banner 主图:告诉浏览器优先下载 -->
<img src="hero.webp" fetchpriority="high" alt="...">

<!-- 折叠线以下的商品图:延迟到用户滚动到附近才加载 -->
<img src="product.webp" loading="lazy" width="600" height="600" alt="...">

fetchpriority="high" 对 LCP 的影响非常直接——浏览器会把这张图的优先级提到最高,不和其他资源竞争带宽。这一个属性单独就能让 LCP 快 0.3–0.8 秒。

同时给 <img> 设置 widthheight 属性,浏览器提前知道图片占位尺寸,消除 CLS 布局偏移。


优化 02 · App 脚本延迟加载(减少 TBT 420ms)

贡献:TBT 从 840ms 降到约 420ms,主线程阻塞减半

每装一个 Shopify App,它就可能往页面首屏注入一段 JS 脚本。这些脚本在浏览器解析 HTML 时同步执行,会阻塞后续内容的渲染——这就是为什么装了很多 App 的店铺,首屏加载特别慢。

在 PageSpeed 报告里点「减少第三方代码」这条建议,能看到每个脚本的大小和阻塞时长。我的情况是:聊天插件和积分 App 各自阻塞了 180ms 以上,导致 Banner 主图的下载被推迟到它们执行完之后才开始——图片被自己的 App 拖慢了。

三个具体操作

① 给非关键 App 脚本加 defer

defer 属性让脚本等 HTML 的 DOM 全部解析完之后再执行,不阻塞首屏渲染:

<!-- 加 defer:等 DOM 解析完再执行,不阻塞首屏 -->
<script src="review-app.js" defer></script>

<!-- 加 async:下载完立即执行,适合完全独立的脚本 -->
<script src="analytics.js" async></script>

defer 保证执行顺序,async 不保证。依赖其他脚本的 App 用 defer,完全独立的统计类脚本可以用 async

② 审查已装的 App,卸载不用的

很多 App 即使在后台关掉功能开关,它的 JS 脚本依然在每个页面加载——唯一干净的方法是直接卸载。进入 Shopify 后台 → Apps,逐个确认,测试期装的、暂时不用的,全部卸载。

③ 聊天插件改为点击后才加载

聊天插件通常有 200–400KB,而且是阻塞加载。优化方案:先渲染一个纯 HTML + CSS 的假气泡按钮,用户点击后才真正加载聊天脚本:

document.querySelector('.fake-chat-btn').addEventListener('click', () => {
  const script = document.createElement('script');
  script.src = 'https://chat-provider.com/widget.js';
  document.head.appendChild(script);
});

这个改动单独就省下约 200ms TBT。真正需要聊天的用户会主动点击,大部分浏览用户完全不需要加载这个脚本。


优化 03 · 字体加载:一行 CSS 消除白屏

贡献:字体相关白屏消除,preconnect 再省约 200ms

Shopify 主题使用自定义字体时,font-display 默认是 block——字体文件下载完成前,浏览器会把所有文字隐藏,页面呈现白屏(FOIT)。用户可能等 2–3 秒什么都看不到。

改成 font-display: swap,浏览器先用系统字体显示文字内容,字体下载完成后再切换。用户立即看到页面,字体会有一次轻微替换,但这比白屏体验好得多。

Google Fonts:在 URL 加 display=swap 参数

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
      rel="stylesheet">

本地字体文件:在 @font-face 里加 font-display: swap

@font-face {
  font-family: 'MyFont';
  src: url('myfont.woff2') format('woff2');
  font-display: swap; /* 先系统字体,加载完再切换 */
}

额外一行 preconnect,再省 200ms

preconnect 告诉浏览器提前与字体服务器建立 TCP 连接(包括 DNS 解析、TCP 握手、TLS 协商)。等到真正需要下载字体时,连接已经建好,直接传输数据:

<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

这一行放在 <head> 最靠前的位置,越早触发越好。


优化 04 · Critical CSS 内联(消除 0.8s 渲染阻塞)

贡献:渲染阻塞消除约 0.8 秒,首屏可视时间提前

外部 CSS 文件是浏览器渲染的阻塞资源——浏览器解析 HTML 遇到 <link rel="stylesheet"> 时,会暂停渲染,等 CSS 文件完全下载并解析完才继续构建页面。

优化前:HTML 解析 → 暂停等 CSS(0.8s)→ 继续渲染首屏
优化后:HTML 解析 → 直接渲染(内联 CSS 已就位)
        同时异步下载完整 CSS → 切换(用户无感知)

Critical CSS 的思路是:把「首屏必须有才能渲染」的那部分样式提取出来,直接内联到 HTML 的 <style> 标签里;其余完整 CSS 文件改为异步加载,不阻塞首屏渲染。

三步操作

① 用工具提取 Critical CSS

推荐用 critical 工具,输入页面 URL,自动分析首屏(viewport 内)所需的 CSS 规则,提取结果通常只有 5–12KB。

② 内联到 layout/theme.liquid

<head>
  <!-- Critical CSS 直接内联,浏览器立即可用 -->
  <style>
    .header { ... }
    .hero-banner { ... }
    .nav { ... }
  </style>

  <!-- 完整主题 CSS 改为异步加载,不阻塞渲染 -->
  <link rel="preload" href="{{ 'theme.css' | asset_url }}"
        as="style"
        onload="this.rel='stylesheet'">
</head>

rel="preload" + onload="this.rel='stylesheet'" 是让 CSS 异步加载的标准写法:先以高优先级下载文件但不阻塞渲染,下载完成后触发 onloadrel 改成 stylesheet 使样式生效。

③ Critical CSS 控制在 14KB 以内

HTTP 第一个 TCP 数据包约 14KB,Critical CSS 超过这个大小需要额外的网络往返才能收到完整数据,反而变慢。超出时需要裁剪,只保留真正首屏可见区域的样式。


各项贡献汇总与优化顺序

做完 4 项优化,各指标变化:

优化项具体效果优先级
图片转 WebP + sizes + fetchpriority体积减少 65%,LCP 7.2s → 1.4s⭐⭐⭐⭐⭐ 先做
App 脚本 defer + 卸载 + 聊天懒加载TBT 减少 420ms⭐⭐⭐⭐ 第二
font-display swap + preconnect字体等待减少约 200ms⭐⭐⭐ 第三
Critical CSS 内联渲染阻塞消除 0.8s⭐⭐⭐ 第四
综合结果首屏 8.4s → 1.9s,PageSpeed 31 → 89 分

不要一上来就做 Critical CSS。 它是四项里实施最复杂的,但收益排在后面。正确顺序是:先压图片(几乎所有慢站最大问题在图片,改动成本极低)→ 再处理脚本(审查 App 列表,卸载加 defer)→ 然后做字体(5 分钟搞定)→ 最后做 Critical CSS(前三步做完分数还不理想时再来)。

💡 建议每完成一项就重新跑一次 PageSpeed,记录分数变化。不同主题、不同 App 配置的站点,各项的收益比例会有差异——用自己的数据验证,比任何参考数字都准确。