打开网页等半天?——浏览器性能优化完全指南

0 阅读4分钟

写给想让网站飞起来的开发者,不讲废话,直接上干货


先说一个让人抓狂的场景

你辛辛苦苦开发了一个网站,兴冲冲地发给朋友测试。

朋友说:“点开要等好几秒,点个按钮还要转圈圈,体验太差了。”

你一看数据:

  • 首屏加载时间:8 秒(人家要求 3 秒以内)
  • LCP(最大内容绘制):6 秒(Google 要求 2.5 秒以内)
  • FID(首次输入延迟):300ms(人家要求 100ms 以内)

你慌了,开始到处搜"怎么让网站变快",搜到一堆专业术语:Tree Shaking、Code Splitting、Lazy Loading、CDN 加速…

看完更懵了,不知道从哪下手。

别慌,这篇文章帮你把性能优化讲清楚,让你知道该从哪下手。


性能优化到底优化什么?

指标名字达标标准体验感
FCPFirst Contentful Paint< 1.8s看到第一个内容
LCPLargest Contentful Paint< 2.5s看到主要内容
FIDFirst Input Delay< 100ms第一次能交互
CLSCumulative Layout Shift< 0.1页面不乱跳
TTITime to Interactive< 3.8s完全可交互

打开 Chrome,按 F12,切到 Lighthouse 标签,点"Analyze Page Load",它会给你打个分,顺便告诉你哪里有问题。


优化思路:三板斧

1. 少加载东西(体积)
2. 加载快一点(速度)
3. 边用边加载(顺序)

第一斧:少加载东西(体积优化)

1. 图片压缩——最大的罪魁祸首

网页加载慢,90% 是图片的问题。

// 用 sharp 压缩图片(Node.js)
import sharp from 'sharp';

await sharp('original.png')
  .resize(1920, 1080)
  .webp({ quality: 80 })
  .toFile('optimized.webp');

图片格式选择:

格式适合场景压缩效果
JPEG照片、复杂图片体积小,有损
PNG需要透明、图标体积大,无损
WebP通用场景体积小 30%,兼容性好
AVIF最新格式体积最小,兼容性差
SVG图标、简单图形体积极小,可缩放
<!-- 用 picture 标签,浏览器自动选最优格式 -->
<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="描述" loading="lazy" width="800" height="600">
</picture>

2. 代码压缩

// vite.config.js
export default defineConfig({
  build: {
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },
    cssMinify: true,
  },
});

3. Tree Shaking——只打包用到的代码

// ❌ 整个 lodash 都打包进来了(500KB+)
import _ from 'lodash';

// ✅ 只打包 debounce(几 KB)
import { debounce } from 'lodash-es';

第二斧:加载快一点(速度优化)

1. CDN 加速

<!-- ✅ 用 CDN,用户从最近的节点拿 -->
<script src="https://cdn.staticfile.org/react/18.2.0/umd/react.production.min.js"></script>

2. 开启 gzip——让带宽省一半

import compression from 'compression';
app.use(compression({ level: 6, threshold: 1024 }));

gzip 能让传输体积减少 60-70%。

3. 缓存策略——第二次访问秒开

app.use((req, res, next) => {
  if (req.path.endsWith('.html')) {
    res.setHeader('Cache-Control', 'no-cache');
  } else {
    // 静态资源缓存一年,文件名带 hash 自然失效
    res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
  }
  next();
});

4. DNS 预解析

<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>

第三斧:边用边加载(顺序优化)

1. 代码分割

// ❌ 所有路由打包成一个文件,进首页就下载整站代码
import ProductList from './pages/ProductList';

// ✅ 按需加载,用到才下载
const ProductList = lazy(() => import('./pages/ProductList'));
const Dashboard   = lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <Routes>
        <Route path="/products"  element={<ProductList />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Suspense>
  );
}

2. 图片懒加载

<!-- 原生懒加载,一行搞定 -->
<img src="banner.jpg" loading="lazy" width="800" height="600">
// 需要精细控制时,用 Intersection Observer
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, { rootMargin: '200px' });

document.querySelectorAll('[data-src]').forEach(img => observer.observe(img));

3. defer 和 async

<!-- ❌ 阻塞渲染,白屏时间变长 -->
<script src="app.js"></script>

<!-- ✅ defer:等 HTML 解析完再执行(推荐) -->
<script src="app.js" defer></script>

<!-- async:适合无依赖的第三方脚本 -->
<script src="analytics.js" async></script>

四个容易踩的坑

坑 1:首屏图片也懒加载了

<!-- ❌ 首屏图片懒加载,LCP 变差 -->
<img src="hero.jpg" loading="lazy">

<!-- ✅ 首屏图片要快,加 fetchpriority -->
<img src="hero.jpg" fetchpriority="high">

坑 2:图片没设宽高,CLS 很差

<!-- ❌ 图片加载后页面跳动 -->
<img src="photo.jpg">

<!-- ✅ 提前留好位置 -->
<img src="photo.jpg" width="800" height="600" loading="lazy">

坑 3:第三方脚本没用 async

<!-- ❌ 统计脚本阻塞渲染 -->
<script src="https://analytics.example.com/track.js"></script>

<!-- ✅ 不影响主流程,用 async -->
<script src="https://analytics.example.com/track.js" async></script>

坑 4:字体加载导致闪烁

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

上线前检查清单

  • 图片压缩了吗?(WebP/AVIF)
  • 首屏图片加了 fetchpriority="high" 吗?
  • 非首屏图片加了 loading="lazy" 吗?
  • 图片设置宽高了吗?(防止 CLS)
  • JS 压缩了吗?(去掉 console)
  • 路由代码分割了吗?
  • CDN 接入了吗?
  • gzip/brotli 开启了吗?
  • 缓存设置了吗?(文件名加 hash)
  • Lighthouse 分数达到 90+ 了吗?

总结

少加载东西 —— 压缩图片、压缩代码、Tree Shaking

加载快一点 —— CDN、gzip 压缩、合理缓存

边用边加载 —— 代码分割、懒加载、预加载

先用 Lighthouse 跑一遍,找到最影响性能的问题,从那个开始改。

一般优化完,首屏时间能从 5-8 秒降到 1-2 秒,用户体验直接翻倍。