JavaScript 性能优化实战:从 3 秒到 300 ms 的压缩与缓存之旅

JavaScript 性能优化实战:从 3 秒到 300 ms 的压缩与缓存之旅

首发于 CSDN · 2025 年 11 月

原创:yu779

转载请注明来源与作者


1. 背景:为什么又要聊性能?

过去两年,我们团队把「首屏时间」从 3 s 压到 300 ms,真实业务、真实数据、真实收益

本文不讲"拍脑袋"的数字,直接上可落地的 10 个策略 ,并给出源码级对比线上 A/B 报告

读完你可以:

  1. 用 DevTools 3 分钟定位瓶颈;
  2. 让 vendors 包体积立减 60 %;
  3. 让二次访问直接 0 网络请求(离线可用)。

2. 指标:先定义"快"

指标 目标 工具
FCP(First Contentful Paint) < 1.0 s Lighthouse
LCP(Largest Contentful Paint) < 1.2 s WebPageTest
TTI(Time to Interactive) < 2.0 s Chrome DevTools
包体积(gzipped) < 200 KB webpack-bundle-analyzer

3. 诊断:一条命令定位"真凶"

bash 复制代码
npx lighthouse https://my-site.com --view --preset=desktop

重点关注:

  • Opportunities → 按"节省体积"排序;
  • Diagnostics → 看 Main-thread Blocking Time;
  • Passed Audits → 反向排除已优化项。

4. 实战 10 策

# 策略 收益 成本 源码片段
1 Tree-Shaking + sideEffects -35 % 5 min 见 4.1
2 SplitChunks 自动分包 -20 % 10 min 见 4.2
3 动态 import() 懒加载 -40 % 20 min 见 4.3
4 HTTP/2 + preload -0.3 s 1 h 见 4.4
5 Brotli 替代 gzip -25 % 30 min 见 4.5
6 Service Worker 缓存 0 请求 1 h 见 4.6
7 图片 WebP + 自适应尺寸 -60 % 40 min 见 4.7
8 减少 polyfill -15 % 15 min 见 4.8
9 长列表虚拟滚动 FPS 55→60 30 min 见 4.9
10 Vite 预构建依赖 HMR < 200 ms 20 min 见 4.10

4.1 Tree-Shaking:让"死"代码消失

背景:lodash 默认全量引入,体积 70 KB。

解决:改成按需 + 标记 sideEffects。

js 复制代码
// webpack.config.js
module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true,      // 启用 Tree-Shaking
    sideEffects: false,     // 告诉 webpack 可以安全删除
  },
};

package.json

json 复制代码
{
  "sideEffects": ["*.css", "*.less"]
}

结果:lodash 从 70 KB → 24 KB(gz)。

4.2 SplitChunks:让 vendors 永不重复

js 复制代码
// webpack.config.js
optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        priority: 10,
        reuseExistingChunk: true,
      },
    },
  },
},

收益:多页应用共用 vendors.xxx.js,二次访问直接 304。

4.3 懒加载:路由级 + 组件级

js 复制代码
// React 路由示例
import { lazy, Suspense } from 'react';
const HeavyChart = lazy(() => import(/* webpackChunkName: "chart" */ '@/components/HeavyChart'));

<Suspense fallback={<Skeleton />}>
  <HeavyChart />
</Suspense>

实测:首屏减少 42 KB(gz),LCP −320 ms。

4.4 HTTP/2 + preload:让关键资源插队

html 复制代码
<!-- 放在 <head> 顶部 -->
<link rel="preload" href="/fonts/Inter.var.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/js/app.xxx.js" as="script">

注意:HTTP/1.1 时代合并,HTTP/2 时代拆细+多路复用收益更大。

4.5 Brotli:一行 Nginx 配置,体积再减 25 %

conf 复制代码
# /etc/nginx/conf.d/gzip.conf
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/javascript application/json image/svg+xml;

4.6 Service Worker:离线也能秒开

用 Workbox 零成本接入:

bash 复制代码
npm i workbox-webpack-plugin -D
js 复制代码
// webpack.config.js
const { GenerateSW } = require('workbox-webpack-plugin');
plugins.push(
  new GenerateSW({
    runtimeCaching: [{
      urlPattern: /^https:\/\/api\.mycdn\.com/,
      handler: 'StaleWhileRevalidate',
      options: { cacheName: 'api-cache', expiration: { maxEntries: 200 } },
    }],
  })
);

效果:二次访问 0 网络请求,TTI 从 1.8 s → 280 ms。

4.7 图片:WebP + 自适应尺寸

html 复制代码
<picture>
  <source srcset="/img/hero-800.webp" media="(max-width: 600px)" type="image/webp">
  <source srcset="/img/hero-1600.webp" media="(min-width: 601px)" type="image/webp">
  <img src="/img/hero-1600.jpg" alt="hero" loading="lazy" width="1600" height="600">
</picture>

收益:同画质下体积 −60 %;Lighthouse「Serve images in next-gen formats」直接满分。

4.8 减少 polyfill:core-js 按需 + browserslist

js 复制代码
// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: 'usage',
      corejs: 3,
      targets: { browsers: ['>0.2%', 'not dead', 'not op_mini all'] },
    }],
  ],
};

结果:core-js 从 35 KB → 8 KB(gz)。

4.9 长列表虚拟滚动:react-window 示例

js 复制代码
import { FixedSizeList } from 'react-window';
<FixedSizeList height={600} itemCount={10000} itemSize={50}>
  {({ index, style }) => <div style={style}>Row {index}</div>}
</FixedSizeList>

FPS:从 38 提升到 60,卡顿消失。

4.10 Vite 预构建:让依赖秒级更新

Vite 默认把 node_modules 预构建成 ESM + HTTP 缓存,HMR 200 ms 内。

技巧:把稳定依赖锁定到 optimizeDeps.include,避免重复扫描。

js 复制代码
// vite.config.ts
export default {
  optimizeDeps: {
    include: ['lodash-es', 'axios', 'dayjs'],
  },
};

5. 监控:把"快"变成常态

  1. Lighthouse CI 接入 GitHub Actions,MR 阶段自动跑分;
  2. Web-Vitals 上报到 Prometheus + Grafana,告警阈值:LCP > 1.5 s;
  3. Bundle-Analyzer 每次构建后输出 diff,体积增长 > 5 % 自动评论提醒。

6. 结果:数据说话

版本 包体积(gz) FCP LCP TTI 离线可用
v1.0 487 KB 2.1 s 2.8 s 3.0 s ×
v2.0 192 KB 0.8 s 1.0 s 1.2 s

注:同一页面、同一服务器、同一时间段 A/B 测试,样本 20 k PV。

7. 常见误区 Top 3

  1. "一把梭"上 HTTP/3 → 先确认内核、CDN、证书链全支持,否则反而 2-RTT 握手退化;
  2. "迷信压缩比" → Brotli 11 级压缩耗时 800 ms,得不偿失,6 级是甜点;
  3. "SW 缓存越多越好" → 缓存策略不清会导致用户永远拿不到新版,版本号 + 主动清理是关键。

总结:性能是"减"出来的

优化不是加东西,而是敢删、会删、删得安全。

把本文 10 策做成 checklist,下次发版前跑一遍,90 分钟换 3 倍性能提升,不香吗?

相关推荐
消失的旧时光-19433 小时前
Kotlinx.serialization 对多态对象(sealed class )支持更好用
java·服务器·前端
少卿3 小时前
React Compiler 完全指南:自动化性能优化的未来
前端·javascript
广州华水科技3 小时前
水库变形监测推荐:2025年单北斗GNSS变形监测系统TOP5,助力基础设施安全
前端
广州华水科技3 小时前
北斗GNSS变形监测一体机在基础设施安全中的应用与优势
前端
七淮3 小时前
umi4暗黑模式设置
前端
8***B3 小时前
前端路由权限控制,动态路由生成
前端
爱隐身的官人4 小时前
beef-xss hook.js访问失败500错误
javascript·xss
军军3604 小时前
从图片到点阵:用JavaScript重现复古数码点阵艺术图
前端·javascript
znhy@1234 小时前
Vue基础知识(一)
前端·javascript·vue.js
terminal0074 小时前
浅谈useRef的使用和渲染机制
前端·react.js·面试