性能优化
独立站首屏 8 秒变 2 秒:我做了这 4 件事
我的 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 已经很明显。最常见的原因是图片没有设置 width 和 height 属性,浏览器不知道预留多少空间。
看完这三个数字,基本就能判断优化重点在哪里。我的情况是 LCP 和 TBT 都很差,意味着图片和 JS 脚本是主要问题,和下面的四项优化完全对应。
优化 01 · 图片格式和懒加载(单项提升最大)
贡献:图片体积减少 65%,LCP 直接从 7.2s 降到约 3.8s
图片是独立站首屏最大的性能杀手,也是单项优化里收益最高的。同样内容、同样质量的图片,不同格式的体积差距极大:
| 格式 | 体积 | 说明 |
|---|---|---|
| JPEG | 420 KB | 多数店铺直接上传原图 |
| PNG | 340 KB | 透明图用 PNG,体积更大 |
| WebP | 145 KB | 同等画质,体积减少 65% |
| AVIF | 105 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> 设置 width 和 height 属性,浏览器提前知道图片占位尺寸,消除 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 异步加载的标准写法:先以高优先级下载文件但不阻塞渲染,下载完成后触发 onload 把 rel 改成 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 配置的站点,各项的收益比例会有差异——用自己的数据验证,比任何参考数字都准确。