Skip to content

前端资源优化

一、资源压缩

前端资源压缩是性能优化的核心环节之一,目标是减少 资源体积传输时间。常见的压缩对象包括:HTML、CSS、JS、图片、字体、SVG、音视频等。

1. JavaScript 压缩

  • 工具Terser(Webpack、Rollup、Vite 常用默认方案)

  • 方法

    • 删除注释、空格、换行
    • 压缩变量名
    • 合并常量表达式
  • 示例(Vite 配置 terser 压缩):

    ts
    // vite.config.ts
    import { defineConfig } from 'vite';
    
    export default defineConfig({
      build: {
        minify: 'terser',
        terserOptions: {
          compress: {
            drop_console: true, // 移除 console.log
            drop_debugger: true, // 移除 debugger
          },
        },
      },
    });

2. CSS 压缩

  • 工具
    • cssnano(PostCSS 插件)
    • clean-css
  • 方法
    • 删除注释、多余空格、重复规则
    • 合并相同选择器
  • 示例(PostCSS 配置 cssnano):
ts
module.exports = {
  plugins: {
    cssnano: { preset: 'default' },
  },
};

3. 图片压缩

3.1. 使用构建工具插件

  • Webpack

    • image-webpack-loader

      支持 pngquant、mozjpeg、svgo 等压缩器。

    • 配合 url-loader / file-loader,小图转 base64,大图走文件。

  • Vite

    • vite-imagemin

      基于 imagemin,支持 PNG、JPEG、GIF、SVG、WebP。

    • 构建时自动压缩,减少最终产物。

3.2. 格式转换

  • 优先使用 WebPAVIF(现代浏览器支持好,压缩率远高于 JPEG/PNG)。

  • 构建阶段可以用:

    • sharp(Node 图片处理库)
    • imagemin-webp
  • 在 HTML 里用 <picture>

    html
    <picture>
      <source srcset="banner.avif" type="image/avif" />
      <source srcset="banner.webp" type="image/webp" />
      <img src="banner.jpg" alt="banner" />
    </picture>

3.3 服务端 & CDN 配合

前端压缩有一定 CPU 消耗,通常还会结合后端和 CDN:

4. 字体压缩

字体子集化,参考:Vite minifont 优化字体插件

二、文件合并

前端实现 文件合并(资源合并) 的核心目标是减少 HTTP 请求次数资源体积,进而优化加载速度。通常应用在 JS、CSS、图片 等静态资源上。

1.为什么要文件合并?

  • 减少请求数:HTTP/1.1 下每个请求有延迟,多个小文件加载很慢。
  • 提高压缩率:合并后再压缩,通常比单独压缩多个小文件效果更好。
  • 缓存优化:合并文件后,缓存策略更容易控制。

2.常见的文件合并方案

2.1 JS & CSS 合并

  • Webpack / Vite / Rollup
    • Webpack 提供 SplitChunksPlugin,可配置将多个小模块合并为一个 bundle。
    • Vite 使用 rollup-plugin,默认也会合并 import 的模块。

例子(Vite Rollup 配置合并 vendor):

t's
// vite.config.ts
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'], // 合并依赖
          utils: ['./src/utils/index.ts'], // 合并工具库
        },
      },
    },
  },
};
  • 手工合并

    • 将多个 CSS 文件合并成一个 styles.css,在构建时统一压缩。
    • 将多个 JS 文件合并成 bundle.js,减少引入。

2.2 图片合并

CSS Sprite(雪碧图)

  • 把多个小图标合并成一张大图,通过 background-position 定位。
  • 优点:减少请求数。
  • 缺点:维护复杂,适合 icon 类资源。
css
.icon {
  background: url('sprite.png') no-repeat;
}
.icon-home {
  background-position: 0 0;
  width: 32px;
  height: 32px;
}
.icon-user {
  background-position: -32px 0;
  width: 32px;
  height: 32px;
}

雪碧图自动化工具

  • sprite-loader (webpack 插件)
  • gulp.spritesmith
  • 这些工具会自动生成 sprite.png 和对应的 CSS/SCSS。

三、资源懒加载和异步加载

前端的 资源懒加载 & 异步加载 是提升页面性能、加快首屏速度的核心手段。常见优化点可以分为 图片 / 脚本 / 样式 / 组件 四大类。

1.图片懒加载 (Lazy Loading Images)

图片往往是页面最大的资源,占用带宽和加载时间,所以必须懒加载。

实现方式

  • 原生属性(现代浏览器支持)
html
<img src="big-image.jpg" loading="lazy" alt="大图" />
  • IntersectionObserver API(兼容性好,推荐)
js
const imgs = document.querySelectorAll('img[data-src]');
const observer = new IntersectionObserver((entries, obs) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      obs.unobserve(img);
    }
  });
});
imgs.forEach((img) => observer.observe(img));
  • 渐进式占位图(LQIP / BlurHash)

  • 先加载一张低清晰度的缩略图当占位

  • 真图加载完替换

2.脚本异步加载 (JS Async/Defer)

  • async

异步加载,加载完成后立即执行(可能打断 HTML 解析)。

html
<script src="analytics.js" async></script>
  • defer异步加载,但会在 HTML 解析完后再按顺序执行。
html
<script src="app.js" defer></script>
  • 动态按需加载
js
document.getElementById('btn').addEventListener('click', () => {
  import('./module.js').then((module) => {
    module.init();
  });
});

3.CSS 按需/异步加载

  • 媒体查询延迟加载
html
<link rel="stylesheet" href="print.css" media="print" />
  • 动态插入样式
js
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/theme-dark.css';
document.head.appendChild(link);

4.组件级懒加载(前端框架支持)

  • Vue 动态组件
vue
<script setup>
const AsyncComp = defineAsyncComponent(() => import('./MyComponent.vue'));
</script>

<template>
  <AsyncComp />
</template>
  • React Lazy + Suspense
js
import React, { lazy, Suspense } from 'react';
const Chart = lazy(() => import('./Chart'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Chart />
    </Suspense>
  );
}

5.资源加载优化思路

  • 首屏关键资源内联(CSS/JS)
  • 非核心资源延迟加载(广告、埋点、地图、三方 SDK)
  • 路由懒加载(SPA 必须)
  • 分包策略(webpack / Vite 动态 import)
  • 预加载 / 预获取
html
<link rel="preload" href="main.js" as="script" /> <link rel="prefetch" href="next-page.js" />

四、CDN

CDN(Content Delivery Network,内容分发网络)是前端性能优化里非常重要的一环,用于 加速资源加载、减轻服务器压力、提升用户体验。我帮你系统梳理一下前端与 CDN 的关系和使用方式:


1. CDN 的基本原理

  • CDN 会在 全球分布的边缘节点 部署缓存服务器。
  • 当用户请求静态资源(JS、CSS、图片、视频等)时,CDN 会就近返回离用户最近的节点资源,而不是直连源站。
  • 好处
    • 减少网络延迟(物理距离近)
    • 减轻源站压力(分担请求)
    • 抗流量攻击(CDN 提供防护)

2. 前端常用的 CDN 资源

  1. 静态资源
    • JS 库:Vue、React、jQuery、Lodash 等
    • CSS 库:Tailwind、Bootstrap
    • 自己项目打包后的 bundle.js、style.css
  2. 媒体资源
    • 图片、视频、音频
  3. 大文件下载
    • PDF、安装包、Excel

3. 前端如何接入 CDN

方式一:直接引用第三方 CDN

<!-- React from jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>

方式二:自己构建 + 上传到 CDN

  • 打包(webpack、Vite 等)
  • 上传产物到 CDN(阿里云 OSS + CDN、七牛云、Cloudflare、AWS CloudFront)
  • HTML/模板中引用 CDN 资源

方式三:动态加载 CDN 资源

(适合 懒加载 或某些依赖不常用时)

js
function loadScript(url) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = url;
    script.onload = resolve;
    script.onerror = reject;
    document.head.appendChild(script);
  });
}

loadScript('https://cdn.jsdelivr.net/npm/lodash/lodash.min.js').then(() => {
  console.log(_.chunk([1, 2, 3, 4], 2));
});

4. 前端优化结合 CDN 的方式

  1. 静态资源上 CDN
    • JS、CSS、图片、字体文件
  2. 长缓存 + 文件名 hash
    • main.[hash].js
    • 浏览器永远缓存,更新时 hash 改变
  3. 分模块拆分
    • 核心代码走包内,第三方库走 CDN
    • 节省打包体积,提升加载速度
  4. 按需加载
    • 懒加载路由、组件
    • 结合 CDN 资源异步加载

5. 注意事项

  • CDN 与版本管理:避免引用最新版本导致不可控
  • HTTPS:必须与页面协议一致(避免混合内容警告)
  • 回源机制:CDN 没缓存时要能从源站获取
  • 国内外选择:国内用阿里云、七牛云,海外可用 Cloudflare、AWS CloudFront

Released under the MIT License.