aurora blog
  • JS

    • 基本汇总
    • Date时间
    • Array方法
    • String 方法
    • Object方法
    • RegExp正则
    • Es6 新特性等
    • Event-Loop
    • Http and Https
    • for in和for of区别
    • Web Worker
    • Promise && async
    • 堆内存 & 栈内存
    • JS设计模式探索
    • npm & yarn
    • Fetch和Axios的区别
    • 学习及面试问题整理
    • URL 输入到页面加载过程
    • 跨域&nginx本地项目代理
  • FE框架

    • react

      • 基本用法
      • react生命周期
      • hooks 16.8版本后
      • Route原理
      • Redux源码解析
      • React16 Fiber
      • React-VirtualDOM
      • React事件委托机制
      • React性能优化
      • 状态管理方案对比
      • React 18新特性
    • vue

      • vue 基本用法
      • vue 生命周期
      • VirtualDOM
      • vue 事件委托
      • vue 架构
      • vue 状态管理
      • 问题等
    • React-Native

      • React-Native 基本用法
    • 微前端

      • 遇到的问题
    • 鸿蒙 harmony

      • harmony 基础
  • 构建工具

    • webpack

      • Webpack配置详解
      • Webpack的使用
      • Babel-polyfill
      • webpack面试题
    • vite

      • vite配置详解
      • vite面试题
    • rollup

      • Rollup配置详解
      • rollup面试题
    • 构建工具对比
  • 性能优化

    • 性能优化策略
    • 缓存策略
    • 性能优化面试题
  • 浏览器

    • 浏览器渲染原理
    • 浏览器缓存机制
    • 浏览器面试题
  • 工程化

    • 代码规范
    • 工程化面试题
  • 前端安全

    • XSS 攻击与防护
    • CSRF 攻击与防护
    • 前端安全面试题
  • 移动端开发

    • 移动端适配
    • 移动端开发面试题
  • 前端监控

    • 错误监控
    • 性能监控
    • 前端监控面试题
  • Typescript

    • Typescript详解
  • Servers

    • Nodejs
    • Nginx
  • Git命令

    • git常用规范
  • 数据库

    • mongodb
    • mongodb
  • Other

    • Jenkins自动化部署

前端性能优化

  • 性能优化概述
  • 加载性能优化
    • 1. 代码分割和懒加载
    • 2. 资源压缩
    • 3. 图片优化
    • 4. 减少 HTTP 请求
    • 5. 使用 CDN
    • 6. 预加载和预连接
    • 7. 缓存策略
  • 运行时性能优化
    • 1. 减少重排和重绘
    • 2. 防抖和节流
    • 3. 虚拟滚动
    • 4. 使用 Web Workers
    • 5. 优化事件处理
  • 资源优化
    • 1. Tree Shaking
    • 2. 按需加载
    • 3. 使用 WebP 和现代格式
  • 首屏优化
    • 1. 关键渲染路径优化
    • 2. 骨架屏
    • 3. 服务端渲染(SSR)
  • 性能监控
    • 1. 性能指标
    • 2. 性能分析工具
  • 最佳实践总结

性能优化概述

前端性能优化是提升用户体验的关键,主要包括:

  • 加载性能:减少首屏加载时间
  • 运行时性能:提升页面交互流畅度
  • 资源优化:减少资源体积和请求数量

加载性能优化

1. 代码分割和懒加载

路由懒加载:

// Vue Router
const routes = [
  {
    path: '/home',
    component: () => import('./views/Home.vue'),
  },
];

// React Router
const Home = lazy(() => import('./views/Home'));

组件懒加载:

// Vue
const HeavyComponent = defineAsyncComponent(() => 
  import('./components/HeavyComponent.vue')
);

// React
const HeavyComponent = lazy(() => import('./components/HeavyComponent'));

动态导入:

// 按需加载
if (condition) {
  import('./module').then(module => {
    module.doSomething();
  });
}

2. 资源压缩

代码压缩:

  • 生产环境启用代码压缩(Terser、UglifyJS)
  • 启用 Gzip/Brotli 压缩
  • 移除 console、debugger

配置示例:

// webpack
optimization: {
  minimize: true,
  minimizer: [
    new TerserPlugin({
      terserOptions: {
        compress: {
          drop_console: true,
          drop_debugger: true,
        },
      },
    }),
  ],
}

// vite
build: {
  minify: 'terser',
  terserOptions: {
    compress: {
      drop_console: true,
      drop_debugger: true,
    },
  },
}

3. 图片优化

图片格式选择:

  • WebP:现代浏览器,体积小
  • AVIF:最新格式,压缩率更高
  • PNG:需要透明背景
  • JPEG:照片类图片
  • SVG:图标、简单图形

图片懒加载:

<!-- 原生懒加载 -->
<img src="image.jpg" loading="lazy" alt="description" />

<!-- Intersection Observer -->
<img data-src="image.jpg" class="lazy" alt="description" />
// 懒加载实现
const images = document.querySelectorAll('img[data-src]');

const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      observer.unobserve(img);
    }
  });
});

images.forEach(img => imageObserver.observe(img));

图片压缩:

  • 使用工具压缩:TinyPNG、ImageOptim
  • 使用 CDN 自动压缩
  • 响应式图片:srcset、sizes
<img 
  srcset="
    image-320w.jpg 320w,
    image-640w.jpg 640w,
    image-1024w.jpg 1024w
  "
  sizes="(max-width: 320px) 280px, (max-width: 640px) 600px, 1024px"
  src="image-1024w.jpg"
  alt="description"
/>

4. 减少 HTTP 请求

合并文件:

  • 合并 CSS 文件
  • 合并 JS 文件(注意代码分割)
  • 使用雪碧图(CSS Sprites)

内联关键资源:

<!-- 内联关键 CSS -->
<style>
  /* 关键 CSS */
</style>

<!-- 内联关键 JS -->
<script>
  // 关键 JS
</script>

5. 使用 CDN

CDN 优势:

  • 就近访问,减少延迟
  • 减轻服务器压力
  • 支持 HTTP/2

配置示例:

// webpack
output: {
  publicPath: 'https://cdn.example.com/',
}

// vite
build: {
  assetsDir: 'assets',
  // 配置 CDN
}

6. 预加载和预连接

资源提示:

<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="https://api.example.com" />

<!-- 预连接 -->
<link rel="preconnect" href="https://api.example.com" />

<!-- 预加载 -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin />

<!-- 预获取 -->
<link rel="prefetch" href="next-page.html" />

<!-- 预渲染 -->
<link rel="prerender" href="next-page.html" />

7. 缓存策略

浏览器缓存:

  • 强缓存:Cache-Control、Expires
  • 协商缓存:ETag、Last-Modified

Service Worker 缓存:

// 缓存策略
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request);
    })
  );
});

运行时性能优化

1. 减少重排和重绘

优化 DOM 操作:

// ❌ 不好:多次重排
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';

// ✅ 好:一次重排
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';

// ✅ 更好:使用 class
element.className = 'new-style';

批量 DOM 操作:

// ❌ 不好
items.forEach(item => {
  container.appendChild(item);
});

// ✅ 好:使用 DocumentFragment
const fragment = document.createDocumentFragment();
items.forEach(item => {
  fragment.appendChild(item);
});
container.appendChild(fragment);

使用 transform 和 opacity:

/* ✅ 触发合成层,不触发重排重绘 */
.element {
  transform: translateX(100px);
  opacity: 0.5;
}

/* ❌ 触发重排 */
.element {
  left: 100px;
  top: 100px;
}

2. 防抖和节流

防抖(Debounce):

function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

// 使用
const handleSearch = debounce((query) => {
  // 搜索逻辑
}, 300);

节流(Throttle):

function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// 使用
const handleScroll = throttle(() => {
  // 滚动处理
}, 100);

3. 虚拟滚动

适用场景:

  • 长列表渲染
  • 大量数据展示

实现思路:

class VirtualScroll {
  constructor(container, items, itemHeight) {
    this.container = container;
    this.items = items;
    this.itemHeight = itemHeight;
    this.visibleCount = Math.ceil(container.clientHeight / itemHeight);
    this.startIndex = 0;
    this.endIndex = this.startIndex + this.visibleCount;
  }
  
  render() {
    const visibleItems = this.items.slice(this.startIndex, this.endIndex);
    // 渲染可见项
  }
  
  handleScroll() {
    const scrollTop = this.container.scrollTop;
    this.startIndex = Math.floor(scrollTop / this.itemHeight);
    this.endIndex = this.startIndex + this.visibleCount;
    this.render();
  }
}

4. 使用 Web Workers

将耗时任务移到 Worker:

// main.js
const worker = new Worker('worker.js');

worker.postMessage({ data: largeData });

worker.onmessage = (e) => {
  const result = e.data;
  // 处理结果
};

// worker.js
self.onmessage = (e) => {
  const { data } = e.data;
  // 处理数据
  const result = processData(data);
  self.postMessage(result);
};

5. 优化事件处理

事件委托:

// ❌ 不好:每个元素绑定事件
items.forEach(item => {
  item.addEventListener('click', handleClick);
});

// ✅ 好:事件委托
container.addEventListener('click', (e) => {
  if (e.target.matches('.item')) {
    handleClick(e);
  }
});

及时移除事件监听:

// 组件卸载时移除
useEffect(() => {
  const handleResize = () => {
    // 处理逻辑
  };
  
  window.addEventListener('resize', handleResize);
  
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

资源优化

1. Tree Shaking

确保 Tree Shaking 生效:

// ✅ 使用 ES modules
import { debounce } from 'lodash-es';

// ❌ 避免导入整个库
import _ from 'lodash';

2. 按需加载

组件库按需加载:

// ✅ 按需导入
import { Button } from 'antd';

// ❌ 避免全量导入
import * as Antd from 'antd';

3. 使用 WebP 和现代格式

图片格式检测:

function supportsWebP() {
  const canvas = document.createElement('canvas');
  canvas.width = 1;
  canvas.height = 1;
  return canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;
}

// 根据支持情况选择格式
const imageFormat = supportsWebP() ? 'webp' : 'jpg';

首屏优化

1. 关键渲染路径优化

优化步骤:

  1. 减少关键资源数量
  2. 减少关键资源大小
  3. 缩短关键渲染路径长度

关键 CSS 内联:

<head>
  <style>
    /* 关键 CSS */
    .header { ... }
    .hero { ... }
  </style>
</head>

2. 骨架屏

实现骨架屏:

<template>
  <div v-if="loading" class="skeleton">
    <div class="skeleton-header"></div>
    <div class="skeleton-content"></div>
  </div>
  <div v-else>
    <!-- 实际内容 -->
  </div>
</template>

3. 服务端渲染(SSR)

优势:

  • 首屏内容直接渲染
  • SEO 友好
  • 减少客户端渲染压力

性能监控

1. 性能指标

Web Vitals:

  • LCP (Largest Contentful Paint):最大内容绘制
  • FID (First Input Delay):首次输入延迟
  • CLS (Cumulative Layout Shift):累积布局偏移

测量方法:

// LCP
new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];
  console.log('LCP:', lastEntry.renderTime || lastEntry.loadTime);
}).observe({ entryTypes: ['largest-contentful-paint'] });

// FID
new PerformanceObserver((list) => {
  const entries = list.getEntries();
  entries.forEach(entry => {
    console.log('FID:', entry.processingStart - entry.startTime);
  });
}).observe({ entryTypes: ['first-input'] });

2. 性能分析工具

Chrome DevTools:

  • Performance 面板
  • Lighthouse
  • Network 面板

API:

// Performance API
const perfData = window.performance.timing;
const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
console.log('页面加载时间:', pageLoadTime);

最佳实践总结

  1. 代码层面

    • 代码分割和懒加载
    • Tree Shaking
    • 减少重排重绘
    • 使用防抖节流
  2. 资源层面

    • 图片优化和懒加载
    • 资源压缩
    • CDN 加速
    • 缓存策略
  3. 网络层面

    • 减少 HTTP 请求
    • 使用 HTTP/2
    • 预加载关键资源
    • 使用 Service Worker
  4. 渲染层面

    • 关键 CSS 内联
    • 骨架屏
    • 虚拟滚动
    • SSR/SSG
最近更新:: 2025/11/20 14:50
Contributors: liyulai
Next
缓存策略