前端性能优化实战

记录一下前端性能优化实战,dailyuse(memoflow) web 端。当前包含首包拆分。

#type / howto #status / growing #tech / dev / frontend

[!info] related notes

前端性能优化实战

目标

系统性优化 memoflow Web 端的前端性能,提升用户体验。重点关注:

  • 首屏加载速度(LCP < 2.5s)
  • 交互响应速度(FID < 100ms)
  • 视觉稳定性(CLS < 0.1)

前置条件

  • 已有可运行的前端应用
  • 了解浏览器渲染流程(解析 → 构建 DOM/CSSOM → 布局 → 绘制)
  • 了解 Chrome DevTools 的 Performance 和 Lighthouse 面板
  • 项目使用 Vue3 / React + Vite 或类似构建工具

核心指标

指标全称含义目标
LCPLargest Contentful Paint最大内容绘制时间< 2.5s
FIDFirst Input Delay首次输入延迟< 100ms
CLSCumulative Layout Shift累积布局偏移< 0.1
TTFBTime to First Byte首字节到达时间< 800ms
FCPFirst Contentful Paint首次内容绘制< 1.8s

优化策略分层

┌─────────────────────────────────┐
│         交互层 (Interaction)      │  ← 事件处理、状态更新、虚拟列表
├─────────────────────────────────┤
│         渲染层 (Rendering)        │  ← 减少重排重绘、CSS 优化、动画
├─────────────────────────────────┤
│         网络层 (Network)          │  ← 代码分割、缓存、CDN、压缩
└─────────────────────────────────┘

步骤

网络层优化

1. 代码分割(Code Splitting)

使用动态 import() 实现路由级懒加载:

// router/index.ts
const routes = [
  {
    path: '/dashboard',
    component: () => import('@/views/Dashboard.vue'), // 懒加载
  },
  {
    path: '/settings',
    component: () => import('@/views/Settings.vue'),
  },
]

Vite 中手动分割 chunk:

// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor-vue': ['vue', 'vue-router', 'pinia'],
          'vendor-ui': ['element-plus'],
          'vendor-echarts': ['echarts'],
        },
      },
    },
  },
})

2. 图片优化

  • 使用 WebP / AVIF 格式替代 PNG/JPG
  • 实现图片懒加载(loading="lazy" 或 Intersection Observer)
  • 使用 <picture> 标签做响应式图片
  • 小图标使用 SVG sprite 或 icon font
<!-- 响应式图片示例 -->
<picture>
  <source srcset="hero.avif" type="image/avif" />
  <source srcset="hero.webp" type="image/webp" />
  <img src="hero.jpg" alt="hero" loading="lazy" />
</picture>

3. 缓存策略

// vite.config.ts
export default defineConfig({
  build: {
    // 内容哈希文件名,支持长期缓存
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name].[hash].js',
        chunkFileNames: 'assets/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash].[ext]',
      },
    },
  },
})

Nginx 缓存配置:

# 静态资源长期缓存
location /assets/ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# HTML 不缓存,保证版本更新
location / {
    expires -1;
    add_header Cache-Control "no-cache";
}

4. Gzip / Brotli 压缩

// vite.config.ts
import compress from 'vite-plugin-compression'

export default defineConfig({
  plugins: [
    compress({ algorithm: 'gzip' }),
    compress({ algorithm: 'brotliCompress' }),
  ],
})

渲染层优化

5. 减少重排重绘

  • 批量 DOM 操作(使用 DocumentFragmentrequestAnimationFrame
  • 避免频繁读取布局属性(offsetWidthscrollTop 等触发强制同步布局)
  • 使用 transformopacity 做动画(仅触发合成层,不触发重排)

6. CSS 优化

  • 避免深层嵌套选择器
  • 使用 contain: layout 限制重排范围
  • 关键 CSS 内联,非关键 CSS 异步加载

交互层优化

7. 虚拟列表

长列表使用虚拟滚动,只渲染可视区域内的元素:

// 使用 vue-virtual-scroller
<virtual-scroller :items="largeList" :item-height="50">
  <template #default="{ item }">
    <div class="list-item">{{ item.name }}</div>
  </template>
</virtual-scroller>

8. 防抖与节流

// 搜索输入防抖
import { useDebounceFn } from '@vueuse/core'

const debouncedSearch = useDebounceFn((keyword: string) => {
  fetchSearchResults(keyword)
}, 300)

// 滚动事件节流
import { useThrottleFn } from '@vueuse/core'

const throttledScroll = useThrottleFn(() => {
  updateScrollPosition()
}, 100)

9. Web Worker 分担计算

将密集计算移到 Worker 线程,避免阻塞主线程:

// worker.ts
self.onmessage = (e) => {
  const result = heavyComputation(e.data)
  self.postMessage(result)
}

// main.ts
const worker = new Worker(new URL('./worker.ts', import.meta.url))
worker.postMessage(data)
worker.onmessage = (e) => {
  // 处理结果
}

验证方式

Lighthouse 审计

# CLI 方式
npx lighthouse https://your-app.com --output html --output-path ./report.html

# Chrome DevTools → Lighthouse tab

Performance 面板

  1. 打开 Chrome DevTools → Performance
  2. 点击录制,执行关键操作
  3. 分析火焰图中的长任务(> 50ms)
  4. 关注 Main 线程的阻塞情况

Web Vitals 监控

import { onLCP, onFID, onCLS } from 'web-vitals'

onLCP(console.log)
onFID(console.log)
onCLS(console.log)

Bundle 分析

npx vite-bundle-visualizer
# 或
npx source-map-explorer dist/assets/*.js

常见问题

Q: 代码分割后首屏反而变慢?

分割点过多会增加 HTTP 请求数。合并小 chunk,确保每个 chunk > 20KB(否则 gzip 后收益不大)。

Q: 图片懒加载导致 CLS 升高?

为图片预留固定尺寸(width + height 属性或 aspect-ratio CSS),避免加载时布局跳动。

Q: 虚拟列表出现白屏闪烁?

确保 item-height 设置正确。对于不定高的列表,使用动态测量 + 缓存高度的方案。

Q: 如何判断优化是否有效?

对比优化前后的 Lighthouse 分数和 Web Vitals 数据。建议在 CI 中集成 Lighthouse CI 持续监控。

创建于 2026/4/6 更新于 2026/5/27