---
description: 前端性能优化和监控
globs: ["**/*.vue", "**/*.ts", "**/*.css", "**/components/**/*"]
alwaysApply: false
---

# 前端性能优化规范

## ⚡ 代码分割和懒加载

### 路由级别代码分割
```typescript
// router/index.ts
import { RouteRecordRaw } from 'vue-router';

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue'),
  },
  {
    path: '/users',
    name: 'Users',
    component: () => import('@/views/Users.vue'),
  },
];

// ✅ 组件懒加载
import { defineAsyncComponent } from 'vue';

const UserModal = defineAsyncComponent({
  loader: () => import('@/components/UserModal.vue'),
  loadingComponent: () => h('div', '加载中...'),
  errorComponent: () => h('div', '加载失败'),
  delay: 200,
  timeout: 3000,
});
```

### 图片懒加载实现
```vue
<template>
  <!-- ✅ 响应式图片实现 -->
  <picture>
    <source 
      srcset="
        /images/hero-320.webp 320w, 
        /images/hero-640.webp 640w, 
        /images/hero-1280.webp 1280w
      " 
      type="image/webp"
    >
    <img 
      src="/images/hero-640.jpg"
      srcset="
        /images/hero-320.jpg 320w, 
        /images/hero-640.jpg 640w, 
        /images/hero-1280.jpg 1280w
      "
      sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
      alt="产品展示图"
      loading="lazy"
      class="w-full h-auto object-cover"
    >
  </picture>
</template>

<script setup lang="ts">
// ✅ 图片懒加载 Composable
export function useLazyImage() {
  const observer = ref<IntersectionObserver | null>(null);
  const isLoaded = ref(false);
  const isInView = ref(false);

  const observeElement = (element: HTMLElement) => {
    if (!observer.value) {
      observer.value = new IntersectionObserver(
        (entries) => {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
              isInView.value = true;
              observer.value?.unobserve(entry.target);
            }
          });
        },
        { threshold: 0.1 }
      );
    }
    observer.value.observe(element);
  };

  onUnmounted(() => {
    observer.value?.disconnect();
  });

  return {
    isLoaded,
    isInView,
    observeElement,
  };
}
</script>
```

## 🗄️ 缓存策略

### Service Worker 缓存
```typescript
// public/sw.js
const CACHE_NAME = 'app-cache-v1';
const urlsToCache = [
  '/',
  '/static/js/bundle.js',
  '/static/css/main.css',
  '/static/images/logo.png',
];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(urlsToCache))
  );
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => {
        if (response) {
          return response;
        }
        
        return fetch(event.request).then((response) => {
          if (!response || response.status !== 200 || response.type !== 'basic') {
            return response;
          }
          
          const responseToCache = response.clone();
          caches.open(CACHE_NAME)
            .then((cache) => {
              cache.put(event.request, responseToCache);
            });
          
          return response;
        });
      })
  );
});
```

### HTTP 缓存配置
```typescript
// ✅ HTTP 缓存配置
const cacheConfig = {
  // 静态资源长期缓存
  static: {
    'Cache-Control': 'public, max-age=31536000, immutable',
  },
  // API 响应适当缓存
  api: {
    'Cache-Control': 'public, max-age=300', // 5分钟
  },
  // HTML 文件不缓存
  html: {
    'Cache-Control': 'no-cache, no-store, must-revalidate',
  },
};
```

## 📊 Web Vitals 监控

### 性能指标收集
```typescript
// utils/performance.ts
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

export function reportWebVitals(onPerfEntry?: (metric: any) => void) {
  if (onPerfEntry && onPerfEntry instanceof Function) {
    getCLS(onPerfEntry);
    getFID(onPerfEntry);
    getFCP(onPerfEntry);
    getLCP(onPerfEntry);
    getTTFB(onPerfEntry);
  }
}

// ✅ 性能指标收集
export function collectPerformanceMetrics() {
  const metrics: any[] = [];

  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      metrics.push({
        name: entry.name,
        value: entry.value,
        timestamp: entry.startTime,
        type: entry.entryType,
      });
    }
  });

  observer.observe({ entryTypes: ['measure', 'navigation', 'paint'] });

  // ✅ 发送性能数据到分析服务
  const sendMetrics = () => {
    if (metrics.length > 0) {
      fetch('/api/analytics/performance', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ metrics }),
      });
    }
  };

  window.addEventListener('beforeunload', sendMetrics);
}
```

## 🎯 关键渲染路径优化

### 关键 CSS 内联
```typescript
// ✅ 关键 CSS 内联
const criticalCSS = `
  /* 首屏关键样式 */
  .header { display: flex; justify-content: space-between; }
  .hero { background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); }
  .button { padding: 12px 24px; border-radius: 6px; }
`;

// ✅ 预加载关键资源
const preloadResources = [
  { href: '/fonts/main.woff2', as: 'font', type: 'font/woff2', crossorigin: 'anonymous' },
  { href: '/images/hero.jpg', as: 'image' },
  { href: '/api/user', as: 'fetch', crossorigin: 'anonymous' },
];

// ✅ 资源提示
const resourceHints = {
  dnsPrefetch: ['//api.example.com', '//cdn.example.com'],
  preconnect: ['//fonts.googleapis.com'],
  preload: preloadResources,
  prefetch: ['/about', '/contact'],
};
```

## 🎯 前端性能最佳实践

### 必需配置项
- ✅ 启用代码分割和懒加载
- ✅ 实现图片懒加载和优化
- ✅ 配置 Service Worker 缓存
- ✅ 设置 Web Vitals 监控
- ✅ 优化关键渲染路径

### 参考文件
- `router/index.ts` - 路由配置
- `utils/performance.ts` - 性能监控工具
- `public/sw.js` - Service Worker 配置
- `vite.config.ts` - 构建配置