code-review
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFrontend Code Review
前端代码评审
This skill provides comprehensive, production-ready code review for modern frontend applications with actionable feedback focused on React/TypeScript/Tailwind stack.
本技能为基于React/TypeScript/Tailwind技术栈的现代前端应用提供全面、可用于生产环境的代码评审,并给出可执行的反馈建议。
Purpose
用途
Transform frontend code review from manual inspection into systematic analysis covering:
- Frontend Security - XSS, CSRF, sensitive data exposure, auth issues
- React Performance - Re-renders, memoization, bundle size, lazy loading
- Code Quality - Readability, maintainability, React best practices
- Component Architecture - Layered architecture, separation of concerns, reusability
- Type Safety - TypeScript usage, type correctness, runtime validation
- Accessibility - WCAG compliance, keyboard navigation, screen readers
- Responsive Design - Mobile-first, breakpoints, Tailwind patterns
- SEO & Meta - Meta tags, semantic HTML, performance metrics
- Testing - Component tests, hooks tests, edge cases
- State Management - Zustand/Context patterns, React Query usage
将前端代码评审从人工检查转变为系统化分析,涵盖以下维度:
- 前端安全 - XSS、CSRF、敏感数据泄露、认证问题
- React性能 - 重渲染、记忆化、包体积、懒加载
- 代码质量 - 可读性、可维护性、React最佳实践
- 组件架构 - 分层架构、关注点分离、可复用性
- 类型安全 - TypeScript使用、类型正确性、运行时验证
- 可访问性 - WCAG合规、键盘导航、屏幕阅读器适配
- 响应式设计 - 移动优先、断点设置、Tailwind模式
- SEO与元信息 - 元标签、语义化HTML、性能指标
- 测试 - 组件测试、Hooks测试、边缘场景
- 状态管理 - Zustand/Context模式、React Query使用
When to Use This Skill
适用场景
Use this skill when:
- User asks for code review or feedback
- User mentions: "review", "check", "feedback", "quality", "security"
- After generating components or features
- User asks about performance or accessibility
- Before committing major changes
- Examples:
- "Review this component"
- "Is this React code optimized?"
- "Can you check for accessibility issues?"
- "How can I improve this?"
- "Review my feature implementation"
当以下情况时使用本技能:
- 用户请求代码评审或反馈
- 用户提及:"review"、"check"、"feedback"、"quality"、"security"(评审、检查、反馈、质量、安全)
- 生成组件或功能后
- 用户询问性能或可访问性相关问题
- 提交重大变更前
- 示例:
- "评审这个组件"
- "这段React代码是否经过优化?"
- "你能检查一下可访问性问题吗?"
- "我该如何改进这段代码?"
- "评审我的功能实现"
Review Process
评审流程
Step 1: Understand Context
步骤1:了解上下文
Before reviewing, gather context:
-
Code Type:
- React Component (UI, Form, List, etc.)
- Custom Hook (business logic)
- Utility function (helpers, transforms)
- API integration (React Query, fetch)
- Store/State management (Zustand, Context)
- Styling (Tailwind, CSS-in-JS)
-
Review Scope:
- Single component/hook
- Entire feature (multiple files)
- Page/route implementation
- Shared utilities
-
Priority:
- Security-critical (auth, payment forms)
- Performance-critical (large lists, complex calculations)
- User-facing (accessibility, UX)
- Internal (utilities, helpers)
在评审前,先收集以下上下文信息:
-
代码类型:
- React组件(UI、表单、列表等)
- 自定义Hook(业务逻辑)
- 工具函数(辅助函数、转换函数)
- API集成(React Query、fetch)
- 存储/状态管理(Zustand、Context)
- 样式(Tailwind、CSS-in-JS)
-
评审范围:
- 单个组件/Hook
- 完整功能(多文件)
- 页面/路由实现
- 共享工具函数
-
优先级:
- 安全关键型(认证、支付表单)
- 性能关键型(大型列表、复杂计算)
- 用户面向型(可访问性、用户体验)
- 内部工具型(工具函数、辅助组件)
Step 2: Initial Scan
步骤2:初步扫描
Quickly scan for obvious issues:
Critical Issues (🚨 CRITICAL):
- XSS vulnerabilities (dangerouslySetInnerHTML)
- CSRF vulnerabilities (missing tokens)
- Sensitive data exposure (tokens in localStorage)
- Authentication bypass
- Hardcoded secrets/API keys
High Priority (⚠️ HIGH):
- Performance bottlenecks (unnecessary re-renders, no memoization)
- Memory leaks (missing cleanup in useEffect)
- Error handling gaps
- Accessibility violations (no ARIA labels, keyboard support)
- Missing input validation
<br>Medium Priority (⚡ MEDIUM):
- Code duplication
- Unclear component/variable names
- Missing loading/error states
- Poor TypeScript usage (any types)
- Inconsistent Tailwind usage
Low Priority (💡 LOW):
- Code style inconsistencies
- Missing comments for complex logic
- Minor optimizations
- Documentation gaps
快速扫描明显问题:
严重问题(🚨 严重):
- XSS漏洞(dangerouslySetInnerHTML)
- CSRF漏洞(缺少令牌)
- 敏感数据泄露(localStorage中存储令牌)
- 认证绕过
- 硬编码密钥/API密钥
高优先级问题(⚠️ 高):
- 性能瓶颈(不必要的重渲染、未使用记忆化)
- 内存泄漏(useEffect中缺少清理逻辑)
- 错误处理缺失
- 可访问性违规(无ARIA标签、无键盘支持)
- 缺少输入验证
中优先级问题(⚡ 中):
- 代码重复
- 组件/变量命名不清晰
- 缺少加载/错误状态
- TypeScript使用不当(any类型)
- Tailwind使用不一致
低优先级问题(💡 低):
- 代码风格不一致
- 复杂逻辑缺少注释
- 次要优化点
- 文档缺失
Step 3: Deep Analysis
步骤3:深度分析
—
3.1 前端安全评审
Perform systematic review across all dimensions:
检查常见前端漏洞:
typescript
// ❌ BAD: XSS vulnerability
function UserComment({ comment }: { comment: string }) {
return <div dangerouslySetInnerHTML={{ __html: comment }} />;
}
// ✅ GOOD: Sanitized HTML
import DOMPurify from 'dompurify';
function UserComment({ comment }: { comment: string }) {
return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment) }} />;
}
// ✅ BETTER: No HTML, just text
function UserComment({ comment }: { comment: string }) {
return <div>{comment}</div>;
}
// ❌ BAD: Token in localStorage (XSS vulnerable)
localStorage.setItem('token', response.token);
// ✅ GOOD: HttpOnly cookie (set by server)
// Or use secure session management library
// ❌ BAD: Hardcoded API key
const API_KEY = "pk_live_abc123xyz";
// ✅ GOOD: Environment variable
const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
// ❌ BAD: No CSRF protection
async function transferMoney(to: string, amount: number) {
await fetch('/api/transfer', {
method: 'POST',
body: JSON.stringify({ to, amount })
});
}
// ✅ GOOD: Include CSRF token
async function transferMoney(to: string, amount: number) {
const csrfToken = getCsrfToken();
await fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({ to, amount })
});
}前端安全检查清单:
- 未在用户输入中使用
dangerouslySetInnerHTML - 未在localStorage中存储敏感令牌
- 无硬编码API密钥或机密信息
- 状态变更操作具备CSRF防护
- 所有表单输入均有验证
- 用户生成内容已做输出清理
- 生产环境强制使用HTTPS
- 控制台日志中无敏感数据
- 受保护路由有正确的认证检查
- 安全的密码输入(敏感字段无自动补全)
3.1 Frontend Security Review
3.2 React性能评审
Check against common frontend vulnerabilities:
typescript
// ❌ BAD: XSS vulnerability
function UserComment({ comment }: { comment: string }) {
return <div dangerouslySetInnerHTML={{ __html: comment }} />;
}
// ✅ GOOD: Sanitized HTML
import DOMPurify from 'dompurify';
function UserComment({ comment }: { comment: string }) {
return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment) }} />;
}
// ✅ BETTER: No HTML, just text
function UserComment({ comment }: { comment: string }) {
return <div>{comment}</div>;
}
// ❌ BAD: Token in localStorage (XSS vulnerable)
localStorage.setItem('token', response.token);
// ✅ GOOD: HttpOnly cookie (set by server)
// Or use secure session management library
// ❌ BAD: Hardcoded API key
const API_KEY = "pk_live_abc123xyz";
// ✅ GOOD: Environment variable
const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
// ❌ BAD: No CSRF protection
async function transferMoney(to: string, amount: number) {
await fetch('/api/transfer', {
method: 'POST',
body: JSON.stringify({ to, amount })
});
}
// ✅ GOOD: Include CSRF token
async function transferMoney(to: string, amount: number) {
const csrfToken = getCsrfToken();
await fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({ to, amount })
});
}Frontend Security Checklist:
- No with user input
dangerouslySetInnerHTML - No sensitive tokens in localStorage
- No hardcoded API keys or secrets
- CSRF protection for state-changing operations
- Input validation on all form inputs
- Output sanitization for user-generated content
- HTTPS enforced (check in production)
- No sensitive data in console.log
- Proper authentication checks on protected routes
- Secure password input (no autocomplete on sensitive fields)
识别性能瓶颈与优化机会:
typescript
// ❌ BAD: Unnecessary re-renders
function ProductList({ products }: { products: Product[] }) {
const sortedProducts = products.sort((a, b) => b.price - a.price);
// Re-sorts on every render!
return (
<div>
{sortedProducts.map(p => (
<ProductCard key={p.id} product={p} onUpdate={() => updateProduct(p.id)} />
))}
</div>
);
}
// ✅ GOOD: Memoized sorting and callbacks
function ProductList({ products }: { products: Product[] }) {
const sortedProducts = useMemo(
() => [...products].sort((a, b) => b.price - a.price),
[products]
);
const handleUpdate = useCallback((id: string) => {
updateProduct(id);
}, []);
return (
<div>
{sortedProducts.map(p => (
<ProductCard key={p.id} product={p} onUpdate={() => handleUpdate(p.id)} />
))}
</div>
);
}
// ❌ BAD: No memoization for expensive child
function ExpensiveChild({ data }: { data: Data }) {
// Complex rendering logic
return <div>{/* ... */}</div>;
}
// ✅ GOOD: Memoized component
const ExpensiveChild = memo(function ExpensiveChild({ data }: { data: Data }) {
// Complex rendering logic
return <div>{/* ... */}</div>;
});
// ❌ BAD: Memory leak - no cleanup
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
// No cleanup!
}, []);
return <div>{count}</div>;
}
// ✅ GOOD: Proper cleanup
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>{count}</div>;
}
// ❌ BAD: No lazy loading for large components
import HeavyChart from './HeavyChart';
import HeavyEditor from './HeavyEditor';
// ✅ GOOD: Lazy loading
const HeavyChart = lazy(() => import('./HeavyChart'));
const HeavyEditor = lazy(() => import('./HeavyEditor'));
function Dashboard() {
return (
<Suspense fallback={<Spinner />}>
<HeavyChart />
<HeavyEditor />
</Suspense>
);
}
// ❌ BAD: Inline object/function props
<Child
config={{ theme: 'dark' }}
onUpdate={() => doSomething()}
/>
// ✅ GOOD: Memoized props
const config = useMemo(() => ({ theme: 'dark' }), []);
const handleUpdate = useCallback(() => doSomething(), []);
<Child config={config} onUpdate={handleUpdate} />React性能检查清单:
- 对昂贵计算使用记忆化(useMemo)
- 为子组件使用记忆化回调(useCallback)
- 对昂贵子组件使用memo()
- 在useEffect中正确清理(定时器、订阅)
- 对大型组件使用懒加载
- 对路由进行代码分割
- 对长列表使用虚拟化(react-window)
- 对频繁事件使用防抖(搜索、滚动)
- 图片优化(next/image、懒加载)
- 避免将内联对象/函数作为props
- 正确使用key(唯一ID,而非索引)
- 避免深层props透传(使用Context/Zustand)
3.2 React Performance Review
3.3 组件架构评审
Identify bottlenecks and optimization opportunities:
typescript
// ❌ BAD: Unnecessary re-renders
function ProductList({ products }: { products: Product[] }) {
const sortedProducts = products.sort((a, b) => b.price - a.price);
// Re-sorts on every render!
return (
<div>
{sortedProducts.map(p => (
<ProductCard key={p.id} product={p} onUpdate={() => updateProduct(p.id)} />
))}
</div>
);
}
// ✅ GOOD: Memoized sorting and callbacks
function ProductList({ products }: { products: Product[] }) {
const sortedProducts = useMemo(
() => [...products].sort((a, b) => b.price - a.price),
[products]
);
const handleUpdate = useCallback((id: string) => {
updateProduct(id);
}, []);
return (
<div>
{sortedProducts.map(p => (
<ProductCard key={p.id} product={p} onUpdate={() => handleUpdate(p.id)} />
))}
</div>
);
}
// ❌ BAD: No memoization for expensive child
function ExpensiveChild({ data }: { data: Data }) {
// Complex rendering logic
return <div>{/* ... */}</div>;
}
// ✅ GOOD: Memoized component
const ExpensiveChild = memo(function ExpensiveChild({ data }: { data: Data }) {
// Complex rendering logic
return <div>{/* ... */}</div>;
});
// ❌ BAD: Memory leak - no cleanup
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
// No cleanup!
}, []);
return <div>{count}</div>;
}
// ✅ GOOD: Proper cleanup
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>{count}</div>;
}
// ❌ BAD: No lazy loading for large components
import HeavyChart from './HeavyChart';
import HeavyEditor from './HeavyEditor';
// ✅ GOOD: Lazy loading
const HeavyChart = lazy(() => import('./HeavyChart'));
const HeavyEditor = lazy(() => import('./HeavyEditor'));
function Dashboard() {
return (
<Suspense fallback={<Spinner />}>
<HeavyChart />
<HeavyEditor />
</Suspense>
);
}
// ❌ BAD: Inline object/function props
<Child
config={{ theme: 'dark' }}
onUpdate={() => doSomething()}
/>
// ✅ GOOD: Memoized props
const config = useMemo(() => ({ theme: 'dark' }), []);
const handleUpdate = useCallback(() => doSomething(), []);
<Child config={config} onUpdate={handleUpdate} />React Performance Checklist:
- Memoization for expensive calculations (useMemo)
- Memoized callbacks (useCallback) for child components
- memo() for expensive child components
- Proper cleanup in useEffect (intervals, subscriptions)
- Lazy loading for heavy components
- Code splitting for routes
- Virtualization for long lists (react-window)
- Debouncing for frequent events (search, scroll)
- Image optimization (next/image, lazy loading)
- No inline objects/functions as props
- Proper key usage (unique IDs, not indexes)
- Avoid deep prop drilling (use Context/Zustand)
检查关注点分离是否合理:
typescript
// ❌ BAD: Everything in one component
function UserProfile() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// API call in component
useEffect(() => {
setLoading(true);
fetch('/api/user')
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, []);
// Business logic in component
const fullName = user ? `${user.firstName} ${user.lastName}` : '';
const isAdult = user?.age >= 18;
// Validation in component
const validateEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{fullName}</div>;
}
// ✅ GOOD: Layered architecture
// api/userApi.ts (Data Access Layer)
export const userApi = {
getUser: async (): Promise<User> => {
const response = await fetch('/api/user');
if (!response.ok) throw new Error('Failed to fetch user');
return response.json();
}
};
// hooks/useUser.ts (Business Logic Layer)
export function useUser() {
const { data: user, isLoading, error } = useQuery({
queryKey: ['user'],
queryFn: userApi.getUser
});
const fullName = user ? `${user.firstName} ${user.lastName}` : '';
const isAdult = user?.age >= 18;
return { user, fullName, isAdult, isLoading, error };
}
// utils/validation.ts (Business Logic Layer)
export const emailSchema = z.string().email();
export function validateEmail(email: string): boolean {
return emailSchema.safeParse(email).success;
}
// components/UserProfile.tsx (Presentation Layer)
export function UserProfile() {
const { fullName, isLoading, error } = useUser();
if (isLoading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return <div className="text-lg font-semibold">{fullName}</div>;
}架构检查清单:
- 分层架构(展示层、业务逻辑层、数据访问层)
- 不在组件中直接调用API
- 不在组件中编写业务逻辑
- 使用Hook封装可复用逻辑
- 使用Zustand/Context管理全局状态
- 使用React Query管理服务端状态
- 无循环依赖
- 遵循单一职责原则
- 组件小巧且聚焦(少于200行)
- 正确定义props类型(TypeScript接口)
3.3 Component Architecture Review
3.4 类型安全评审
Check for proper separation of concerns:
typescript
// ❌ BAD: Everything in one component
function UserProfile() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// API call in component
useEffect(() => {
setLoading(true);
fetch('/api/user')
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, []);
// Business logic in component
const fullName = user ? `${user.firstName} ${user.lastName}` : '';
const isAdult = user?.age >= 18;
// Validation in component
const validateEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{fullName}</div>;
}
// ✅ GOOD: Layered architecture
// api/userApi.ts (Data Access Layer)
export const userApi = {
getUser: async (): Promise<User> => {
const response = await fetch('/api/user');
if (!response.ok) throw new Error('Failed to fetch user');
return response.json();
}
};
// hooks/useUser.ts (Business Logic Layer)
export function useUser() {
const { data: user, isLoading, error } = useQuery({
queryKey: ['user'],
queryFn: userApi.getUser
});
const fullName = user ? `${user.firstName} ${user.lastName}` : '';
const isAdult = user?.age >= 18;
return { user, fullName, isAdult, isLoading, error };
}
// utils/validation.ts (Business Logic Layer)
export const emailSchema = z.string().email();
export function validateEmail(email: string): boolean {
return emailSchema.safeParse(email).success;
}
// components/UserProfile.tsx (Presentation Layer)
export function UserProfile() {
const { fullName, isLoading, error } = useUser();
if (isLoading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return <div className="text-lg font-semibold">{fullName}</div>;
}Architecture Checklist:
- Layered architecture (Presentation, Business Logic, Data Access)
- No API calls directly in components
- No business logic in components
- Hooks for reusable logic
- Zustand/Context for global state
- React Query for server state
- No circular dependencies
- Single Responsibility Principle
- Components are small and focused (<200 lines)
- Proper prop types (TypeScript interfaces)
检查TypeScript使用是否规范:
typescript
// ❌ BAD: Using 'any'
function processData(data: any) {
return data.value;
}
// ✅ GOOD: Proper types
interface ProcessData {
value: string;
count: number;
}
function processData(data: ProcessData): string {
return data.value;
}
// ❌ BAD: Non-null assertion without justification
function getUser(users: User[], id: string) {
return users.find(u => u.id === id)!.name; // Dangerous!
}
// ✅ GOOD: Proper null handling
function getUser(users: User[], id: string): string | null {
return users.find(u => u.id === id)?.name ?? null;
}
// ❌ BAD: No runtime validation
function LoginForm() {
const handleSubmit = (data: unknown) => {
// Assuming data structure without validation
login(data);
};
}
// ✅ GOOD: Runtime validation with Zod
import { z } from 'zod';
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
type LoginData = z.infer<typeof loginSchema>;
function LoginForm() {
const handleSubmit = (data: unknown) => {
try {
const validatedData = loginSchema.parse(data);
login(validatedData); // Type-safe
} catch (err) {
// Handle validation errors
}
};
}
// ❌ BAD: Implicit any in event handlers
<input onChange={(e) => setValue(e.target.value)} />
// 'e' is implicitly 'any' in some configs
// ✅ GOOD: Explicit types
<input onChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)} />
// ✅ BETTER: Typed handler
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
<input onChange={handleChange} />类型安全检查清单:
- 不使用'any'类型(除非确实必要)
- 无正当理由不使用非空断言(!)
- 正确定义props的接口/类型
- 对外部数据使用Zod进行运行时验证
- 对判别联合使用类型守卫
- 为事件处理器显式定义类型
- 在tsconfig.json中启用严格模式
- 无隐式any类型
- 在Hook中正确使用泛型
3.4 Type Safety Review
3.5 可访问性评审
Check for proper TypeScript usage:
typescript
// ❌ BAD: Using 'any'
function processData(data: any) {
return data.value;
}
// ✅ GOOD: Proper types
interface ProcessData {
value: string;
count: number;
}
function processData(data: ProcessData): string {
return data.value;
}
// ❌ BAD: Non-null assertion without justification
function getUser(users: User[], id: string) {
return users.find(u => u.id === id)!.name; // Dangerous!
}
// ✅ GOOD: Proper null handling
function getUser(users: User[], id: string): string | null {
return users.find(u => u.id === id)?.name ?? null;
}
// ❌ BAD: No runtime validation
function LoginForm() {
const handleSubmit = (data: unknown) => {
// Assuming data structure without validation
login(data);
};
}
// ✅ GOOD: Runtime validation with Zod
import { z } from 'zod';
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
type LoginData = z.infer<typeof loginSchema>;
function LoginForm() {
const handleSubmit = (data: unknown) => {
try {
const validatedData = loginSchema.parse(data);
login(validatedData); // Type-safe
} catch (err) {
// Handle validation errors
}
};
}
// ❌ BAD: Implicit any in event handlers
<input onChange={(e) => setValue(e.target.value)} />
// 'e' is implicitly 'any' in some configs
// ✅ GOOD: Explicit types
<input onChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)} />
// ✅ BETTER: Typed handler
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
<input onChange={handleChange} />Type Safety Checklist:
- No 'any' types (except where truly necessary)
- No non-null assertions (!) without justification
- Proper interface/type definitions for props
- Runtime validation with Zod for external data
- Type guards for discriminated unions
- Explicit types for event handlers
- Strict mode enabled in tsconfig.json
- No implicit any
- Proper generic usage in hooks
检查是否符合WCAG 2.1 AA标准:
typescript
// ❌ BAD: No keyboard support, no ARIA labels
<div onClick={handleClick}>
<input type="text" placeholder="Name" />
<div className="text-red-500">Error message</div>
</div>
// ✅ GOOD: Semantic HTML, ARIA labels, keyboard support
<form onSubmit={handleSubmit}>
<label htmlFor="name" className="block text-sm font-medium">
Name
</label>
<input
id="name"
type="text"
aria-label="Enter your name"
aria-describedby="name-error"
aria-invalid={hasError}
className="mt-1 block w-full"
/>
{hasError && (
<p id="name-error" role="alert" className="text-red-500 text-sm">
Error message
</p>
)}
<button type="submit" className="mt-4 px-4 py-2 bg-blue-500">
Submit
</button>
</form>
// ❌ BAD: No alt text, poor color contrast
<div className="bg-gray-200 text-gray-300">
<img src="/icon.png" />
Click here
</div>
// ✅ GOOD: Alt text, proper contrast (WCAG AA)
<div className="bg-gray-900 text-white">
<img src="/icon.png" alt="Settings icon" />
<button className="text-lg font-medium">
Open Settings
</button>
</div>
// ❌ BAD: Custom select without keyboard navigation
function CustomSelect({ options }) {
const [open, setOpen] = useState(false);
return (
<div onClick={() => setOpen(!open)}>
{open && options.map(opt => (
<div onClick={() => selectOption(opt)}>{opt}</div>
))}
</div>
);
}
// ✅ GOOD: Accessible custom select
function CustomSelect({ options }: { options: string[] }) {
const [open, setOpen] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'ArrowDown') {
setSelectedIndex(i => Math.min(i + 1, options.length - 1));
} else if (e.key === 'ArrowUp') {
setSelectedIndex(i => Math.max(i - 1, 0));
} else if (e.key === 'Enter') {
selectOption(options[selectedIndex]);
}
};
return (
<div
role="combobox"
aria-expanded={open}
aria-haspopup="listbox"
tabIndex={0}
onKeyDown={handleKeyDown}
onClick={() => setOpen(!open)}
>
{open && (
<ul role="listbox">
{options.map((opt, index) => (
<li
key={opt}
role="option"
aria-selected={index === selectedIndex}
onClick={() => selectOption(opt)}
>
{opt}
</li>
))}
</ul>
)}
</div>
);
}可访问性检查清单:
- 使用语义化HTML元素(button、nav、main等)
- 为交互元素添加ARIA标签
- 支持键盘导航(Tab、Enter、方向键)
- 焦点管理(可见的焦点状态)
- 颜色对比度符合WCAG AA标准(文本为4.5:1)
- 所有图片均有替代文本
- 表单标签正确关联(htmlFor)
- 错误消息使用role="alert"
- 无键盘陷阱
- 提供主内容跳转链接
- 标题按逻辑顺序排列(h1、h2、h3)
- 交互元素具备正确的role属性
3.5 Accessibility Review
3.6 响应式设计评审
Check WCAG 2.1 AA compliance:
typescript
// ❌ BAD: No keyboard support, no ARIA labels
<div onClick={handleClick}>
<input type="text" placeholder="Name" />
<div className="text-red-500">Error message</div>
</div>
// ✅ GOOD: Semantic HTML, ARIA labels, keyboard support
<form onSubmit={handleSubmit}>
<label htmlFor="name" className="block text-sm font-medium">
Name
</label>
<input
id="name"
type="text"
aria-label="Enter your name"
aria-describedby="name-error"
aria-invalid={hasError}
className="mt-1 block w-full"
/>
{hasError && (
<p id="name-error" role="alert" className="text-red-500 text-sm">
Error message
</p>
)}
<button type="submit" className="mt-4 px-4 py-2 bg-blue-500">
Submit
</button>
</form>
// ❌ BAD: No alt text, poor color contrast
<div className="bg-gray-200 text-gray-300">
<img src="/icon.png" />
Click here
</div>
// ✅ GOOD: Alt text, proper contrast (WCAG AA)
<div className="bg-gray-900 text-white">
<img src="/icon.png" alt="Settings icon" />
<button className="text-lg font-medium">
Open Settings
</button>
</div>
// ❌ BAD: Custom select without keyboard navigation
function CustomSelect({ options }) {
const [open, setOpen] = useState(false);
return (
<div onClick={() => setOpen(!open)}>
{open && options.map(opt => (
<div onClick={() => selectOption(opt)}>{opt}</div>
))}
</div>
);
}
// ✅ GOOD: Accessible custom select
function CustomSelect({ options }: { options: string[] }) {
const [open, setOpen] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'ArrowDown') {
setSelectedIndex(i => Math.min(i + 1, options.length - 1));
} else if (e.key === 'ArrowUp') {
setSelectedIndex(i => Math.max(i - 1, 0));
} else if (e.key === 'Enter') {
selectOption(options[selectedIndex]);
}
};
return (
<div
role="combobox"
aria-expanded={open}
aria-haspopup="listbox"
tabIndex={0}
onKeyDown={handleKeyDown}
onClick={() => setOpen(!open)}
>
{open && (
<ul role="listbox">
{options.map((opt, index) => (
<li
key={opt}
role="option"
aria-selected={index === selectedIndex}
onClick={() => selectOption(opt)}
>
{opt}
</li>
))}
</ul>
)}
</div>
);
}Accessibility Checklist:
- Semantic HTML elements (button, nav, main, etc.)
- ARIA labels for interactive elements
- Keyboard navigation support (Tab, Enter, Arrows)
- Focus management (visible focus states)
- Color contrast meets WCAG AA (4.5:1 for text)
- Alt text for all images
- Form labels properly associated (htmlFor)
- Error messages use role="alert"
- No keyboard traps
- Skip links for main content
- Headings in logical order (h1, h2, h3)
- Interactive elements have proper roles
检查Tailwind/响应式模式是否规范:
typescript
// ❌ BAD: Fixed widths, no responsive classes
<div className="w-800 h-600">
<img src="/hero.jpg" className="w-full" />
</div>
// ✅ GOOD: Responsive Tailwind classes
<div className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<img
src="/hero.jpg"
alt="Hero"
className="w-full h-auto object-cover md:h-96 lg:h-[500px]"
/>
</div>
// ❌ BAD: No mobile considerations
<div className="flex gap-8">
<Sidebar />
<MainContent />
</div>
// ✅ GOOD: Mobile-first responsive layout
<div className="flex flex-col lg:flex-row gap-4 lg:gap-8">
<aside className="w-full lg:w-64">
<Sidebar />
</aside>
<main className="flex-1">
<MainContent />
</main>
</div>
// ❌ BAD: Fixed font sizes
<h1 className="text-32">Title</h1>
// ✅ GOOD: Responsive typography
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold">
Title
</h1>响应式设计检查清单:
- 采用移动优先的设计思路(基础样式适配移动端)
- 使用正确的断点(sm、md、lg、xl、2xl)
- 使用灵活布局(flex、grid)
- 响应式图片(w-full h-auto、object-fit)
- 响应式排版(响应式文本大小)
- 触控友好的点击区域(最小44x44px)
- 移动端无横向滚动
- 在多种屏幕尺寸上测试
3.6 Responsive Design Review
3.7 代码质量评审
Check Tailwind/responsive patterns:
typescript
// ❌ BAD: Fixed widths, no responsive classes
<div className="w-800 h-600">
<img src="/hero.jpg" className="w-full" />
</div>
// ✅ GOOD: Responsive Tailwind classes
<div className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<img
src="/hero.jpg"
alt="Hero"
className="w-full h-auto object-cover md:h-96 lg:h-[500px]"
/>
</div>
// ❌ BAD: No mobile considerations
<div className="flex gap-8">
<Sidebar />
<MainContent />
</div>
// ✅ GOOD: Mobile-first responsive layout
<div className="flex flex-col lg:flex-row gap-4 lg:gap-8">
<aside className="w-full lg:w-64">
<Sidebar />
</aside>
<main className="flex-1">
<MainContent />
</main>
</div>
// ❌ BAD: Fixed font sizes
<h1 className="text-32">Title</h1>
// ✅ GOOD: Responsive typography
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold">
Title
</h1>Responsive Design Checklist:
- Mobile-first approach (base styles for mobile)
- Proper breakpoints (sm, md, lg, xl, 2xl)
- Flexible layouts (flex, grid)
- Responsive images (w-full h-auto, object-fit)
- Responsive typography (responsive text sizes)
- Touch-friendly hit areas (min 44x44px)
- No horizontal scrolling on mobile
- Tested on multiple screen sizes
评估代码的可读性与可维护性:
typescript
// ❌ BAD: Unclear names, deeply nested
function p(d) {
if (d) {
if (d.u) {
if (d.u.n) {
if (d.u.n.length > 0) {
return d.u.n;
}
}
}
}
return 'Anonymous';
}
// ✅ GOOD: Clear names, early returns
function getUserName(data: UserData | null): string {
if (!data?.user?.name) return 'Anonymous';
if (data.user.name.length === 0) return 'Anonymous';
return data.user.name;
}
// ✅ BETTER: Optional chaining
function getUserName(data: UserData | null): string {
return data?.user?.name || 'Anonymous';
}
// ❌ BAD: Magic numbers and strings
if (user.age > 18 && status === 'active') {
grantAccess();
}
// ✅ GOOD: Named constants
const MINIMUM_AGE = 18;
const USER_STATUS = {
ACTIVE: 'active',
INACTIVE: 'inactive'
} as const;
if (user.age > MINIMUM_AGE && status === USER_STATUS.ACTIVE) {
grantAccess();
}
// ❌ BAD: Long component with multiple responsibilities
function UserDashboard() {
// 300 lines of code handling:
// - User data fetching
// - Analytics tracking
// - Notification handling
// - UI rendering
}
// ✅ GOOD: Split into focused components
function UserDashboard() {
return (
<div>
<UserProfile />
<UserAnalytics />
<UserNotifications />
</div>
);
}代码质量检查清单:
- 使用清晰、描述性的命名(如getUserName,而非gn)
- 不使用魔法数字或字符串(使用常量)
- 使用提前返回而非深层嵌套
- 小巧的函数/组件(少于100行)
- 遵循单一职责原则
- 遵循DRY原则(避免重复代码)
- 无注释掉的代码
- 生产代码中无console.log
- 代码风格一致
- 正确的错误处理
3.7 Code Quality Review
3.8 React Hooks最佳实践
Evaluate readability and maintainability:
typescript
// ❌ BAD: Unclear names, deeply nested
function p(d) {
if (d) {
if (d.u) {
if (d.u.n) {
if (d.u.n.length > 0) {
return d.u.n;
}
}
}
}
return 'Anonymous';
}
// ✅ GOOD: Clear names, early returns
function getUserName(data: UserData | null): string {
if (!data?.user?.name) return 'Anonymous';
if (data.user.name.length === 0) return 'Anonymous';
return data.user.name;
}
// ✅ BETTER: Optional chaining
function getUserName(data: UserData | null): string {
return data?.user?.name || 'Anonymous';
}
// ❌ BAD: Magic numbers and strings
if (user.age > 18 && status === 'active') {
grantAccess();
}
// ✅ GOOD: Named constants
const MINIMUM_AGE = 18;
const USER_STATUS = {
ACTIVE: 'active',
INACTIVE: 'inactive'
} as const;
if (user.age > MINIMUM_AGE && status === USER_STATUS.ACTIVE) {
grantAccess();
}
// ❌ BAD: Long component with multiple responsibilities
function UserDashboard() {
// 300 lines of code handling:
// - User data fetching
// - Analytics tracking
// - Notification handling
// - UI rendering
}
// ✅ GOOD: Split into focused components
function UserDashboard() {
return (
<div>
<UserProfile />
<UserAnalytics />
<UserNotifications />
</div>
);
}Code Quality Checklist:
- Clear, descriptive names (getUserName, not gn)
- No magic numbers or strings (use constants)
- Early returns instead of deep nesting
- Small functions/components (<100 lines)
- Single Responsibility Principle
- DRY (Don't Repeat Yourself)
- No commented-out code
- No console.log in production code
- Consistent code style
- Proper error handling
typescript
// ❌ BAD: Missing dependencies
useEffect(() => {
fetchData(userId, filter);
}, []); // userId and filter are missing!
// ✅ GOOD: All dependencies included
useEffect(() => {
fetchData(userId, filter);
}, [userId, filter]);
// ❌ BAD: Object dependency (recreated every render)
const options = { sort: 'asc', limit: 10 };
useEffect(() => {
fetchData(options);
}, [options]); // Will run every render!
// ✅ GOOD: Memoized object
const options = useMemo(() => ({
sort: 'asc',
limit: 10
}), []);
useEffect(() => {
fetchData(options);
}, [options]);
// ❌ BAD: Stale closure
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1); // Uses stale count!
}, 1000);
return () => clearInterval(interval);
}, []); // Empty deps, count is stale
return <div>{count}</div>;
}
// ✅ GOOD: Functional update
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1); // Uses current count
}, 1000);
return () => clearInterval(interval);
}, []); // Empty deps OK now
return <div>{count}</div>;
}3.8 React Hooks Best Practices
步骤4:提供结构化反馈
typescript
// ❌ BAD: Missing dependencies
useEffect(() => {
fetchData(userId, filter);
}, []); // userId and filter are missing!
// ✅ GOOD: All dependencies included
useEffect(() => {
fetchData(userId, filter);
}, [userId, filter]);
// ❌ BAD: Object dependency (recreated every render)
const options = { sort: 'asc', limit: 10 };
useEffect(() => {
fetchData(options);
}, [options]); // Will run every render!
// ✅ GOOD: Memoized object
const options = useMemo(() => ({
sort: 'asc',
limit: 10
}), []);
useEffect(() => {
fetchData(options);
}, [options]);
// ❌ BAD: Stale closure
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1); // Uses stale count!
}, 1000);
return () => clearInterval(interval);
}, []); // Empty deps, count is stale
return <div>{count}</div>;
}
// ✅ GOOD: Functional update
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1); // Uses current count
}, 1000);
return () => clearInterval(interval);
}, []); // Empty deps OK now
return <div>{count}</div>;
}清晰格式化评审结果:
markdown
undefinedStep 4: Provide Structured Feedback
Code Review: [Component/Feature Name]
—
Summary
Format review results clearly:
markdown
undefined[Brief overall assessment: Excellent/Good/Needs Improvement/Critical Issues]
Code Review: [Component/Feature Name]
Critical Issues 🚨
Summary
—
[Brief overall assessment: Excellent/Good/Needs Improvement/Critical Issues]
- [Issue Title] - [file:line]
- Problem: [Description]
- Impact: [Security/Performance/Accessibility]
- Fix:
typescript// Suggested fix
Critical Issues 🚨
High Priority ⚠️
- [Issue Title] - [file:line]
- Problem: [Description]
- Impact: [Security/Performance/Accessibility]
- Fix:
typescript// Suggested fix
[Same format]
High Priority ⚠️
Medium Priority ⚡
[Same format]
[Same format]
Medium Priority ⚡
Low Priority 💡
[Same format]
[Same format]
Low Priority 💡
What's Good ✅
[Same format]
- [Highlight positive aspects]
- [Good practices used]
What's Good ✅
Recommendations
- [Highlight positive aspects]
- [Good practices used]
- [Actionable improvement]
- [Actionable improvement]
Recommendations
Overall Score
- [Actionable improvement]
- [Actionable improvement]
- Security: 8/10
- Performance: 7/10
- Code Quality: 9/10
- Accessibility: 6/10
- Type Safety: 8/10
Overall: 7.6/10
undefinedOverall Score
与其他技能的集成
—
与feature-builder集成
- Security: 8/10
- Performance: 7/10
- Code Quality: 9/10
- Accessibility: 6/10
- Type Safety: 8/10
Overall: 7.6/10
undefined- 评审完整功能(UI + 业务逻辑 + API)
- 确保分层架构正确实现
Integration with Other Skills
与react-component-generator集成
With feature-builder
—
- Review complete features (UI + business logic + API)
- Ensure layered architecture is properly implemented
- 评审生成的组件
- 检查模板使用与定制情况
With react-component-generator
与ui-analyzer集成
- Review generated components
- Check template usage and customization
- 评审从设计截图生成的UI代码
- 验证响应式设计实现
With ui-analyzer
最佳实践
- Review UI code generated from design screenshots
- Verify responsive design implementation
- 建设性反馈: 始终提供带有代码示例的可执行修复方案
- 优先级排序: 优先关注严重问题(安全、可访问性)
- 解释原因: 帮助用户理解问题的影响与背后的逻辑
- 展示前后对比: 提供清晰的代码示例
- 具体明确: 引用具体的行号与文件
- 平衡反馈: 同时突出代码的优点
- 贴合实际: 考虑项目约束与截止日期
- 传递知识: 必要时讲解React/前端相关概念
Best Practices
严重等级
- Be Constructive: Always provide actionable fixes with code examples
- Prioritize: Focus on critical issues (security, accessibility) first
- Explain Why: Help user understand the reasoning and impact
- Show Before/After: Provide clear code examples
- Be Specific: Reference exact lines and files
- Balance: Highlight what's good too
- Be Practical: Consider project constraints and deadlines
- Educate: Explain React/frontend concepts when needed
- 🚨 严重: 安全漏洞(XSS、CSRF)、数据泄露、程序崩溃
- ⚠️ 高优先级: 性能问题(内存泄漏)、可访问性违规、用户体验损坏
- ⚡ 中优先级: 代码质量、可维护性、缺失TypeScript类型
- 💡 低优先级: 代码风格、文档、次要优化
Severity Levels
参考文件
- 🚨 CRITICAL: Security vulnerabilities (XSS, CSRF), data exposure, crashes
- ⚠️ HIGH: Performance issues (memory leaks), accessibility violations, broken UX
- ⚡ MEDIUM: Code quality, maintainability, missing TypeScript types
- 💡 LOW: Code style, documentation, minor optimizations
- - 前端安全最佳实践
references/frontend-security.md - - React模式与反模式
references/react-patterns.md - - WCAG合规指南
references/accessibility-guide.md
本技能可实现全面、专业的前端代码评审,提升代码质量、安全性与用户体验!
Reference Files
—
- - Frontend security best practices
references/frontend-security.md - - React patterns and anti-patterns
references/react-patterns.md - - WCAG compliance guide
references/accessibility-guide.md
This skill enables thorough, professional frontend code reviews that improve code quality, security, and user experience!
—