fe-perf
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFE Performance Optimization
前端性能优化
$ARGUMENTS分析并优化通过传递的目标的性能。
$ARGUMENTS분석 절차
分析流程
- 대상 파악: 파일 또는 "bundle" 키워드에 따라 분석 범위를 결정한다
- 코드 분석: 성능 안티패턴을 식별한다
- 최적화 제안: 우선순위별로 개선안을 제시한다
- 적용: 승인된 최적화를 적용한다
- 确定分析目标:根据文件或"bundle"关键词确定分析范围
- 代码分析:识别性能反模式
- 优化建议:按优先级提出改进方案
- 实施优化:应用已确认的优化方案
성능 최적화 카테고리
性能优化分类
1. 렌더링 최적화
1. 渲染优化
불필요한 리렌더링 방지
避免不必要的重渲染
tsx
// Bad — 매 렌더마다 새 객체 생성
function Parent() {
return <Child style={{ color: "red" }} data={[1, 2, 3]} />;
}
// Good — 안정적 참조
const style = { color: "red" };
const data = [1, 2, 3];
function Parent() {
return <Child style={style} data={data} />;
}tsx
// 不良示例 — 每次渲染都会创建新对象
function Parent() {
return <Child style={{ color: "red" }} data={[1, 2, 3]} />;
}
// 良好示例 — 稳定的引用
const style = { color: "red" };
const data = [1, 2, 3];
function Parent() {
return <Child style={style} data={data} />;
}React.memo 적절한 사용
合理使用React.memo
tsx
// 비용이 큰 컴포넌트에만 적용
const ExpensiveList = memo(function ExpensiveList({ items }: Props) {
return items.map((item) => <ExpensiveItem key={item.id} item={item} />);
});tsx
// 仅对性能开销大的组件使用
const ExpensiveList = memo(function ExpensiveList({ items }: Props) {
return items.map((item) => <ExpensiveItem key={item.id} item={item} />);
});useMemo / useCallback
useMemo / useCallback
tsx
// 비용이 큰 계산에 useMemo
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// memo된 자식에 전달하는 콜백에 useCallback
const handleSelect = useCallback((id: string) => {
setSelectedId(id);
}, []);tsx
// 对性能开销大的计算使用useMemo
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// 对传递给已缓存子组件的回调使用useCallback
const handleSelect = useCallback((id: string) => {
setSelectedId(id);
}, []);2. 코드 분할 & Lazy Loading
2. 代码分割 & 懒加载
라우트 기반 코드 분할
基于路由的代码分割
Next.js App Router는 자동으로 라우트별 코드 분할을 적용한다. 추가 최적화:
tsx
// 무거운 컴포넌트 lazy loading
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("@/components/HeavyChart"), {
loading: () => <Skeleton className="h-[400px]" />,
ssr: false, // 클라이언트 전용
});Next.js App Router会自动按路由进行代码分割。额外优化方案:
tsx
// 对重型组件进行懒加载
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("@/components/HeavyChart"), {
loading: () => <Skeleton className="h-[400px]" />,
ssr: false, // 仅客户端渲染
});조건부 import
条件式导入
tsx
// Bad — 항상 로드
import { PDFViewer } from "react-pdf";
// Good — 필요할 때만 로드
const PDFViewer = dynamic(() =>
import("react-pdf").then((mod) => ({ default: mod.PDFViewer }))
);tsx
// 不良示例 — 始终加载
import { PDFViewer } from "react-pdf";
// 良好示例 — 仅在需要时加载
const PDFViewer = dynamic(() =>
import("react-pdf").then((mod) => ({ default: mod.PDFViewer }))
);3. 번들 사이즈 최적화
3. 包体积优化
트리쉐이킹 확인
确认Tree Shaking生效
typescript
// Bad — 전체 라이브러리 import
import _ from "lodash";
_.debounce(fn, 300);
// Good — 개별 함수 import
import debounce from "lodash/debounce";
debounce(fn, 300);
// Best — 작은 대안 사용
import { useDebouncedCallback } from "use-debounce";typescript
// 不良示例 — 导入整个库
import _ from "lodash";
_.debounce(fn, 300);
// 良好示例 — 导入单个函数
import debounce from "lodash/debounce";
debounce(fn, 300);
// 最佳方案 — 使用轻量替代库
import { useDebouncedCallback } from "use-debounce";번들 분석
包体积分析
bash
undefinedbash
undefinedVite 번들 분석
Vite包体积分析
npx vite-bundle-visualizer
npx vite-bundle-visualizer
Next.js 번들 분석
Next.js包体积分析
ANALYZE=true next build # @next/bundle-analyzer 필요
undefinedANALYZE=true next build # 需要安装@next/bundle-analyzer
undefined무거운 의존성 교체 가이드
重型依赖替换指南
| 무거운 라이브러리 | 가벼운 대안 |
|---|---|
| |
| 개별 import 또는 ES 네이티브 메서드 |
| |
| |
| |
| 重型依赖库 | 轻量替代方案 |
|---|---|
| |
| 单个函数导入或原生ES方法 |
| |
| |
| |
4. 이미지 최적화
4. 图片优化
tsx
// Next.js Image 컴포넌트 사용
import Image from "next/image";
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // LCP 이미지에 priority
placeholder="blur" // 블러 플레이스홀더
sizes="(max-width: 768px) 100vw, 50vw" // 반응형 크기
/>
// 아이콘 — SVG inline 또는 sprite
import { LucideIcon } from "lucide-react"; // 트리쉐이킹 지원tsx
// 使用Next.js Image组件
import Image from "next/image";
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // 为LCP图片设置priority
placeholder="blur" // 模糊占位符
sizes="(max-width: 768px) 100vw, 50vw" // 响应式尺寸
/>
// 图标 — 使用内联SVG或雪碧图
import { LucideIcon } from "lucide-react"; // 支持Tree Shaking5. Server Component 최적화
5. Server Component优化
tsx
// Server Component에서 데이터 가져오기 (제로 번들)
async function ProductList() {
const products = await db.product.findMany();
return products.map((p) => <ProductCard key={p.id} product={p} />);
}
// 병렬 데이터 페칭
async function Dashboard() {
const [users, orders, stats] = await Promise.all([
getUsers(),
getOrders(),
getStats(),
]);
// ...
}tsx
// 在Server Component中获取数据(零打包体积)
async function ProductList() {
const products = await db.product.findMany();
return products.map((p) => <ProductCard key={p.id} product={p} />);
}
// 并行数据获取
async function Dashboard() {
const [users, orders, stats] = await Promise.all([
getUsers(),
getOrders(),
getStats(),
]);
// ...
}6. 리스트 가상화
6. 列表虚拟化
tsx
// 100+ 아이템 리스트에 가상화 적용
import { useVirtualizer } from "@tanstack/react-virtual";
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
return (
<div ref={parentRef} className="h-[500px] overflow-auto">
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
height: virtualItem.size,
transform: `translateY(${virtualItem.start}px)`,
}}
>
<ListItem item={items[virtualItem.index]} />
</div>
))}
</div>
</div>
);
}tsx
// 对100+条目的列表应用虚拟化
import { useVirtualizer } from "@tanstack/react-virtual";
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
return (
<div ref={parentRef} className="h-[500px] overflow-auto">
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
height: virtualItem.size,
transform: `translateY(${virtualItem.start}px)`,
}}
>
<ListItem item={items[virtualItem.index]} />
</div>
))}
</div>
</div>
);
}7. 캐싱 전략
7. 缓存策略
typescript
// React Query 캐싱
const { data } = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000, // 5분간 fresh
gcTime: 30 * 60 * 1000, // 30분간 캐시 유지
});
// Next.js fetch 캐싱
const data = await fetch("/api/data", {
next: { revalidate: 3600 }, // 1시간 ISR
});
// Next.js unstable_cache
import { unstable_cache } from "next/cache";
const getCachedData = unstable_cache(
async () => db.query(),
["cache-key"],
{ revalidate: 3600 }
);typescript
// React Query缓存
const { data } = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000, // 5分钟内视为新鲜数据
gcTime: 30 * 60 * 1000, // 缓存保留30分钟
});
// Next.js fetch缓存
const data = await fetch("/api/data", {
next: { revalidate: 3600 }, // 1小时重新验证(ISR)
});
// Next.js unstable_cache
import { unstable_cache } from "next/cache";
const getCachedData = unstable_cache(
async () => db.query(),
["cache-key"],
{ revalidate: 3600 }
);8. Web Vitals 체크포인트
8. Web Vitals检查点
| 지표 | 목표 | 최적화 포인트 |
|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | 히어로 이미지 priority, 폰트 preload |
| FID/INP (Interaction to Next Paint) | < 200ms | 무거운 연산 분리, useDeferredValue |
| CLS (Cumulative Layout Shift) | < 0.1 | 이미지 width/height 지정, 스켈레톤 |
| TTFB (Time to First Byte) | < 800ms | SSG/ISR, 에지 캐싱 |
| 指标 | 目标值 | 优化要点 |
|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | 英雄图设置priority、预加载字体 |
| FID/INP (Interaction to Next Paint) | < 200ms | 分离重型计算、使用useDeferredValue |
| CLS (Cumulative Layout Shift) | < 0.1 | 为图片指定宽高、使用骨架屏 |
| TTFB (Time to First Byte) | < 800ms | 使用SSG/ISR、边缘缓存 |
리포트 형식
报告格式
markdown
undefinedmarkdown
undefinedPerformance Audit: [대상]
性能审计报告: [分析目标]
요약
摘要
- 주요 이슈: N개
- 예상 개선 효과: [번들 -NKB, 렌더링 -Nms 등]
- 主要问题:N个
- 预期优化效果:[包体积减少NKB、渲染速度提升Nms等]
High Impact (우선 적용)
高优先级(优先实施)
[P1] 이슈 제목
[P1] 问题标题
- 영향: [번들 사이즈 / 렌더링 / 로딩 속도]
- 현재: 설명
- 개선안: 코드
- 影响范围:[包体积 / 渲染 / 加载速度]
- 当前状态:问题描述
- 优化方案:代码示例
Medium Impact
中优先级
...
...
Low Impact
低优先级
...
undefined...
undefined실행 규칙
执行规则
- 인자가 없으면 사용자에게 분석 대상을 질문한다
- "bundle" 인자 시 , build 설정을 분석한다
package.json - 과도한 최적화를 경계한다 (측정 가능한 이슈에 집중)
- 최적화 전후 차이를 구체적 수치로 설명한다
- 若无参数,则询问用户确定分析目标
- 当参数为"bundle"时,分析和构建配置
package.json - 避免过度优化,聚焦可量化的性能问题
- 用具体数值说明优化前后的差异