performance-optimization
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePerformance Optimization
性能优化
When to use this skill
何时使用此技能
- 느린 페이지 로드: Lighthouse 점수 낮음
- 느린 렌더링: 사용자 인터랙션 지연
- 큰 번들 크기: 다운로드 시간 증가
- 느린 쿼리: 데이터베이스 병목
- 页面加载缓慢:Lighthouse评分较低
- 渲染缓慢:用户交互延迟
- 包体积过大:下载时间增加
- 查询缓慢:数据库瓶颈
Instructions
操作步骤
Step 1: 성능 측정
步骤1:性能测量
Lighthouse (Chrome DevTools):
bash
undefinedLighthouse (Chrome DevTools):
bash
undefinedCLI
CLI
npm install -g lighthouse
lighthouse https://example.com --view
npm install -g lighthouse
lighthouse https://example.com --view
CI에서 자동화
CI中自动化
lighthouse https://example.com --output=json --output-path=./report.json
**Web Vitals 측정** (React):
```typescript
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric: any) {
// Google Analytics, Datadog 등으로 전송
console.log(metric);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);lighthouse https://example.com --output=json --output-path=./report.json
**Web Vitals测量** (React):
```typescript
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric: any) {
// 发送至Google Analytics、Datadog等
console.log(metric);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);Step 2: React 최적화
步骤2:React优化
React.memo (불필요한 리렌더링 방지):
tsx
// ❌ 나쁜 예: 부모가 리렌더링될 때마다 자식도 리렌더링
function ExpensiveComponent({ data }: { data: Data }) {
return <div>{/* 복잡한 렌더링 */}</div>;
}
// ✅ 좋은 예: props 변경 시에만 리렌더링
const ExpensiveComponent = React.memo(({ data }: { data: Data }) => {
return <div>{/* 복잡한 렌더링 */}</div>;
});useMemo & useCallback:
tsx
function ProductList({ products, category }: Props) {
// ✅ 필터링 결과 메모이제이션
const filteredProducts = useMemo(() => {
return products.filter(p => p.category === category);
}, [products, category]);
// ✅ 콜백 메모이제이션
const handleAddToCart = useCallback((id: string) => {
addToCart(id);
}, []);
return (
<div>
{filteredProducts.map(product => (
<ProductCard key={product.id} product={product} onAdd={handleAddToCart} />
))}
</div>
);
}Lazy Loading & Code Splitting:
tsx
import { lazy, Suspense } from 'react';
// ✅ Route-based code splitting
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
// ✅ Component-based lazy loading
const HeavyChart = lazy(() => import('./components/HeavyChart'));
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<Skeleton />}>
<HeavyChart data={data} />
</Suspense>
</div>
);
}React.memo(避免不必要的重渲染):
tsx
// ❌ 反面示例:父组件重渲染时子组件也跟着重渲染
function ExpensiveComponent({ data }: { data: Data }) {
return <div>{/* 复杂渲染逻辑 */}</div>;
}
// ✅ 正面示例:仅在props变化时重渲染
const ExpensiveComponent = React.memo(({ data }: { data: Data }) => {
return <div>{/* 复杂渲染逻辑 */}</div>;
});useMemo & useCallback:
tsx
function ProductList({ products, category }: Props) {
// ✅ 对过滤结果进行记忆化
const filteredProducts = useMemo(() => {
return products.filter(p => p.category === category);
}, [products, category]);
// ✅ 对回调函数进行记忆化
const handleAddToCart = useCallback((id: string) => {
addToCart(id);
}, []);
return (
<div>
{filteredProducts.map(product => (
<ProductCard key={product.id} product={product} onAdd={handleAddToCart} />
))}
</div>
);
}懒加载与代码分割:
tsx
import { lazy, Suspense } from 'react';
// ✅ 基于路由的代码分割
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
// ✅ 基于组件的懒加载
const HeavyChart = lazy(() => import('./components/HeavyChart'));
function Dashboard() {
return (
<div>
<h1>仪表盘</h1>
<Suspense fallback={<Skeleton />}>
<HeavyChart data={data} />
</Suspense>
</div>
);
}Step 3: 번들 크기 최적화
步骤3:包体积优化
Webpack Bundle Analyzer:
bash
npm install --save-dev webpack-bundle-analyzerWebpack Bundle Analyzer:
bash
npm install --save-dev webpack-bundle-analyzerpackage.json
package.json
{
"scripts": {
"analyze": "webpack-bundle-analyzer build/stats.json"
}
}
**Tree Shaking (사용하지 않는 코드 제거)**:
```typescript
// ❌ 나쁜 예: 전체 라이브러리 임포트
import _ from 'lodash';
// ✅ 좋은 예: 필요한 것만 임포트
import debounce from 'lodash/debounce';Dynamic Imports:
typescript
// ✅ 필요할 때만 로드
button.addEventListener('click', async () => {
const { default: Chart } = await import('chart.js');
new Chart(ctx, config);
});{
"scripts": {
"analyze": "webpack-bundle-analyzer build/stats.json"
}
}
**Tree Shaking(移除未使用代码)**:
```typescript
// ❌ 反面示例:导入整个库
import _ from 'lodash';
// ✅ 正面示例:仅导入所需部分
import debounce from 'lodash/debounce';动态导入:
typescript
// ✅ 仅在需要时加载
button.addEventListener('click', async () => {
const { default: Chart } = await import('chart.js');
new Chart(ctx, config);
});Step 4: 이미지 최적화
步骤4:图片优化
Next.js Image 컴포넌트:
tsx
import Image from 'next/image';
function ProductImage() {
return (
<Image
src="/product.jpg"
alt="Product"
width={500}
height={500}
priority // LCP 이미지인 경우
placeholder="blur" // 블러 플레이스홀더
sizes="(max-width: 768px) 100vw, 50vw"
/>
);
}WebP 포맷 사용:
html
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="Fallback">
</picture>Next.js Image组件:
tsx
import Image from 'next/image';
function ProductImage() {
return (
<Image
src="/product.jpg"
alt="产品"
width={500}
height={500}
priority // 若为LCP图片
placeholder="blur" // 模糊占位图
sizes="(max-width: 768px) 100vw, 50vw"
/>
);
}使用WebP格式:
html
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="降级备用图">
</picture>Step 5: 데이터베이스 쿼리 최적화
步骤5:数据库查询优化
N+1 쿼리 문제 해결:
typescript
// ❌ 나쁜 예: N+1 queries
const posts = await db.post.findMany();
for (const post of posts) {
const author = await db.user.findUnique({ where: { id: post.authorId } });
// 101번 쿼리 (1 + 100)
}
// ✅ 좋은 예: JOIN 또는 include
const posts = await db.post.findMany({
include: {
author: true
}
});
// 1번 쿼리인덱스 추가:
sql
-- 느린 쿼리 식별
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
-- 인덱스 추가
CREATE INDEX idx_users_email ON users(email);
-- 복합 인덱스
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);캐싱 (Redis):
typescript
async function getUserProfile(userId: string) {
// 1. 캐시 확인
const cached = await redis.get(`user:${userId}`);
if (cached) {
return JSON.parse(cached);
}
// 2. DB 조회
const user = await db.user.findUnique({ where: { id: userId } });
// 3. 캐시 저장 (1시간)
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}解决N+1查询问题:
typescript
// ❌ 反面示例:N+1查询
const posts = await db.post.findMany();
for (const post of posts) {
const author = await db.user.findUnique({ where: { id: post.authorId } });
// 共101次查询(1 + 100)
}
// ✅ 正面示例:使用JOIN或include
const posts = await db.post.findMany({
include: {
author: true
}
});
// 仅1次查询添加索引:
sql
-- 识别慢查询
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
-- 添加索引
CREATE INDEX idx_users_email ON users(email);
-- 复合索引
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);缓存(Redis):
typescript
async function getUserProfile(userId: string) {
// 1. 检查缓存
const cached = await redis.get(`user:${userId}`);
if (cached) {
return JSON.parse(cached);
}
// 2. 查询数据库
const user = await db.user.findUnique({ where: { id: userId } });
// 3. 存入缓存(有效期1小时)
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}Output format
输出格式
성능 최적화 체크리스트
性能优化检查表
markdown
undefinedmarkdown
undefinedFrontend
前端
- React.memo로 불필요한 리렌더링 방지
- useMemo/useCallback 적절히 사용
- Lazy loading & Code splitting
- 이미지 최적화 (WebP, lazy loading)
- 번들 크기 분석 및 감소
- 使用React.memo避免不必要的重渲染
- 合理使用useMemo/useCallback
- 懒加载与代码分割
- 图片优化(WebP、懒加载)
- 包体积分析与缩减
Backend
后端
- N+1 쿼리 제거
- 데이터베이스 인덱스 추가
- Redis 캐싱
- API Response 압축 (gzip)
- CDN 사용
- 消除N+1查询
- 添加数据库索引
- Redis缓存
- API响应压缩(gzip)
- 使用CDN
측정
测量
- Lighthouse 점수 90+
- LCP < 2.5s
- FID < 100ms
- CLS < 0.1
undefined- Lighthouse评分达到90+
- LCP < 2.5秒
- FID < 100毫秒
- CLS < 0.1
undefinedConstraints
约束条件
필수 규칙 (MUST)
必须遵守的规则(MUST)
- 측정 먼저: 추측하지 말고 프로파일링
- 점진적 개선: 한 번에 하나씩 최적화
- 성능 모니터링: 지속적으로 추적
- 先测量再优化:不要凭猜测,先进行性能分析
- 逐步优化:一次只优化一个点
- 持续监控性能:持续跟踪性能指标
금지 사항 (MUST NOT)
禁止事项(MUST NOT)
- 조기 최적화: 병목이 없는데 최적화하지 않음
- 가독성 희생: 성능을 위해 코드를 복잡하게 만들지 않음
- 过早优化:在没有瓶颈的情况下不要进行优化
- 牺牲可读性:不要为了性能而让代码变得复杂难懂
Best practices
最佳实践
- 80/20 법칙: 20% 노력으로 80% 개선
- 사용자 중심: 실제 사용자 경험 개선에 집중
- 자동화: CI에서 성능 회귀 테스트
- 80/20法则:用20%的努力获得80%的性能提升
- 以用户为中心:专注于改善实际用户体验
- 自动化:在CI中进行性能回归测试
References
参考资料
Metadata
元数据
버전
版本
- 현재 버전: 1.0.0
- 최종 업데이트: 2025-01-01
- 호환 플랫폼: Claude, ChatGPT, Gemini
- 当前版本:1.0.0
- 最后更新:2025-01-01
- 兼容平台:Claude, ChatGPT, Gemini
관련 스킬
相关技能
- database-schema-design
- ui-components
- database-schema-design
- ui-components
태그
标签
#performance#optimization#React#caching#lazy-loading#web-vitals#code-quality#performance#optimization#React#caching#lazy-loading#web-vitals#code-qualityExamples
示例
Example 1: Basic usage
示例1:基础用法
<!-- Add example content here -->
<!-- 在此添加示例内容 -->
Example 2: Advanced usage
示例2:高级用法
<!-- Add advanced example content here -->
<!-- 在此添加高级示例内容 -->