senior-frontend
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSenior Frontend
高级前端开发
Frontend development patterns, performance optimization, and automation tools for React/Next.js applications.
适用于React/Next.js应用的前端开发模式、性能优化与自动化工具。
Table of Contents
目录
Project Scaffolding
项目搭建
Generate a new Next.js or React project with TypeScript, Tailwind CSS, and best practice configurations.
生成一个包含TypeScript、Tailwind CSS和最佳实践配置的新Next.js或React项目。
Workflow: Create New Frontend Project
工作流:创建新前端项目
-
Run the scaffolder with your project name and template:bash
python scripts/frontend_scaffolder.py my-app --template nextjs -
Add optional features (auth, api, forms, testing, storybook):bash
python scripts/frontend_scaffolder.py dashboard --template nextjs --features auth,api -
Navigate to the project and install dependencies:bash
cd my-app && npm install -
Start the development server:bash
npm run dev
-
使用项目名称和模板运行搭建脚本:bash
python scripts/frontend_scaffolder.py my-app --template nextjs -
添加可选功能(认证、API、表单、测试、Storybook):bash
python scripts/frontend_scaffolder.py dashboard --template nextjs --features auth,api -
进入项目目录并安装依赖:bash
cd my-app && npm install -
启动开发服务器:bash
npm run dev
Scaffolder Options
搭建工具选项
| Option | Description |
|---|---|
| Next.js 14+ with App Router and Server Components |
| React + Vite with TypeScript |
| Add NextAuth.js authentication |
| Add React Query + API client |
| Add React Hook Form + Zod validation |
| Add Vitest + Testing Library |
| Preview files without creating them |
| 选项 | 描述 |
|---|---|
| 带有App Router和Server Components的Next.js 14+ |
| React + Vite + TypeScript |
| 添加NextAuth.js认证功能 |
| 添加React Query + API客户端 |
| 添加React Hook Form + Zod验证 |
| 添加Vitest + Testing Library |
| 预览文件而不实际创建 |
Generated Structure (Next.js)
生成的项目结构(Next.js)
my-app/
├── app/
│ ├── layout.tsx # Root layout with fonts
│ ├── page.tsx # Home page
│ ├── globals.css # Tailwind + CSS variables
│ └── api/health/route.ts
├── components/
│ ├── ui/ # Button, Input, Card
│ └── layout/ # Header, Footer, Sidebar
├── hooks/ # useDebounce, useLocalStorage
├── lib/ # utils (cn), constants
├── types/ # TypeScript interfaces
├── tailwind.config.ts
├── next.config.js
└── package.jsonmy-app/
├── app/
│ ├── layout.tsx # 包含字体的根布局
│ ├── page.tsx # 首页
│ ├── globals.css # Tailwind + CSS变量
│ └── api/health/route.ts
├── components/
│ ├── ui/ # 按钮、输入框、卡片
│ └── layout/ # 头部、底部、侧边栏
├── hooks/ # useDebounce、useLocalStorage
├── lib/ # 工具函数(cn)、常量
├── types/ # TypeScript接口
├── tailwind.config.ts
├── next.config.js
└── package.jsonComponent Generation
组件生成
Generate React components with TypeScript, tests, and Storybook stories.
生成带有TypeScript、测试用例和Storybook故事的React组件。
Workflow: Create a New Component
工作流:创建新组件
-
Generate a client component:bash
python scripts/component_generator.py Button --dir src/components/ui -
Generate a server component:bash
python scripts/component_generator.py ProductCard --type server -
Generate with test and story files:bash
python scripts/component_generator.py UserProfile --with-test --with-story -
Generate a custom hook:bash
python scripts/component_generator.py FormValidation --type hook
-
生成客户端组件:bash
python scripts/component_generator.py Button --dir src/components/ui -
生成服务器组件:bash
python scripts/component_generator.py ProductCard --type server -
生成包含测试和故事文件的组件:bash
python scripts/component_generator.py UserProfile --with-test --with-story -
生成自定义Hook:bash
python scripts/component_generator.py FormValidation --type hook
Generator Options
生成工具选项
| Option | Description |
|---|---|
| Client component with 'use client' (default) |
| Async server component |
| Custom React hook |
| Include test file |
| Include Storybook story |
| Create in output dir without subdirectory |
| Preview without creating files |
| 选项 | 描述 |
|---|---|
| 带有'use client'的客户端组件(默认) |
| 异步服务器组件 |
| 自定义React Hook |
| 包含测试文件 |
| 包含Storybook故事文件 |
| 在输出目录直接创建,不生成子目录 |
| 预览文件而不实际创建 |
Generated Component Example
生成的组件示例
tsx
'use client';
import { useState } from 'react';
import { cn } from '@/lib/utils';
interface ButtonProps {
className?: string;
children?: React.ReactNode;
}
export function Button({ className, children }: ButtonProps) {
return (
<div className={cn('', className)}>
{children}
</div>
);
}tsx
'use client';
import { useState } from 'react';
import { cn } from '@/lib/utils';
interface ButtonProps {
className?: string;
children?: React.ReactNode;
}
export function Button({ className, children }: ButtonProps) {
return (
<div className={cn('', className)}>
{children}
</div>
);
}Bundle Analysis
包分析
Analyze package.json and project structure for bundle optimization opportunities.
分析package.json和项目结构,寻找包优化的机会。
Workflow: Optimize Bundle Size
工作流:优化包大小
-
Run the analyzer on your project:bash
python scripts/bundle_analyzer.py /path/to/project -
Review the health score and issues:
Bundle Health Score: 75/100 (C) HEAVY DEPENDENCIES: moment (290KB) Alternative: date-fns (12KB) or dayjs (2KB) lodash (71KB) Alternative: lodash-es with tree-shaking -
Apply the recommended fixes by replacing heavy dependencies.
-
Re-run with verbose mode to check import patterns:bash
python scripts/bundle_analyzer.py . --verbose
-
在项目上运行分析脚本:bash
python scripts/bundle_analyzer.py /path/to/project -
查看健康评分和问题:
包健康评分:75/100(C级) 大型依赖: moment (290KB) 替代方案:date-fns (12KB) 或 dayjs (2KB) lodash (71KB) 替代方案:支持摇树优化的lodash-es -
应用推荐的修复方案,替换大型依赖。
-
启用详细模式重新运行,检查导入模式:bash
python scripts/bundle_analyzer.py . --verbose
Bundle Score Interpretation
包评分解读
| Score | Grade | Action |
|---|---|---|
| 90-100 | A | Bundle is well-optimized |
| 80-89 | B | Minor optimizations available |
| 70-79 | C | Replace heavy dependencies |
| 60-69 | D | Multiple issues need attention |
| 0-59 | F | Critical bundle size problems |
| 评分 | 等级 | 操作建议 |
|---|---|---|
| 90-100 | A | 包已优化到位 |
| 80-89 | B | 可进行小幅优化 |
| 70-79 | C | 替换大型依赖 |
| 60-69 | D | 需关注多个问题 |
| 0-59 | F | 存在严重的包大小问题 |
Heavy Dependencies Detected
检测到的大型依赖
The analyzer identifies these common heavy packages:
| Package | Size | Alternative |
|---|---|---|
| moment | 290KB | date-fns (12KB) or dayjs (2KB) |
| lodash | 71KB | lodash-es with tree-shaking |
| axios | 14KB | Native fetch or ky (3KB) |
| jquery | 87KB | Native DOM APIs |
| @mui/material | Large | shadcn/ui or Radix UI |
分析工具会识别这些常见的大型包:
| 包名 | 大小 | 替代方案 |
|---|---|---|
| moment | 290KB | date-fns (12KB) 或 dayjs (2KB) |
| lodash | 71KB | 支持摇树优化的lodash-es |
| axios | 14KB | 原生fetch 或 ky (3KB) |
| jquery | 87KB | 原生DOM API |
| @mui/material | 体积大 | shadcn/ui 或 Radix UI |
React Patterns
React模式
Reference:
references/react_patterns.md参考:
references/react_patterns.mdCompound Components
复合组件
Share state between related components:
tsx
const Tabs = ({ children }) => {
const [active, setActive] = useState(0);
return (
<TabsContext.Provider value={{ active, setActive }}>
{children}
</TabsContext.Provider>
);
};
Tabs.List = TabList;
Tabs.Panel = TabPanel;
// Usage
<Tabs>
<Tabs.List>
<Tabs.Tab>One</Tabs.Tab>
<Tabs.Tab>Two</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>Content 1</Tabs.Panel>
<Tabs.Panel>Content 2</Tabs.Panel>
</Tabs>在相关组件之间共享状态:
tsx
const Tabs = ({ children }) => {
const [active, setActive] = useState(0);
return (
<TabsContext.Provider value={{ active, setActive }}>
{children}
</TabsContext.Provider>
);
};
Tabs.List = TabList;
Tabs.Panel = TabPanel;
// 使用方式
<Tabs>
<Tabs.List>
<Tabs.Tab>标签一</Tabs.Tab>
<Tabs.Tab>标签二</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>内容1</Tabs.Panel>
<Tabs.Panel>内容2</Tabs.Panel>
</Tabs>Custom Hooks
自定义Hook
Extract reusable logic:
tsx
function useDebounce<T>(value: T, delay = 500): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage
const debouncedSearch = useDebounce(searchTerm, 300);提取可复用逻辑:
tsx
function useDebounce<T>(value: T, delay = 500): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// 使用方式
const debouncedSearch = useDebounce(searchTerm, 300);Render Props
渲染属性
Share rendering logic:
tsx
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url).then(r => r.json()).then(setData).finally(() => setLoading(false));
}, [url]);
return render({ data, loading });
}
// Usage
<DataFetcher
url="/api/users"
render={({ data, loading }) =>
loading ? <Spinner /> : <UserList users={data} />
}
/>共享渲染逻辑:
tsx
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url).then(r => r.json()).then(setData).finally(() => setLoading(false));
}, [url]);
return render({ data, loading });
}
// 使用方式
<DataFetcher
url="/api/users"
render={({ data, loading }) =>
loading ? <Spinner /> : <UserList users={data} />
}
/>Next.js Optimization
Next.js优化
Reference:
references/nextjs_optimization_guide.md参考:
references/nextjs_optimization_guide.mdServer vs Client Components
服务器组件 vs 客户端组件
Use Server Components by default. Add 'use client' only when you need:
- Event handlers (onClick, onChange)
- State (useState, useReducer)
- Effects (useEffect)
- Browser APIs
tsx
// Server Component (default) - no 'use client'
async function ProductPage({ params }) {
const product = await getProduct(params.id); // Server-side fetch
return (
<div>
<h1>{product.name}</h1>
<AddToCartButton productId={product.id} /> {/* Client component */}
</div>
);
}
// Client Component
'use client';
function AddToCartButton({ productId }) {
const [adding, setAdding] = useState(false);
return <button onClick={() => addToCart(productId)}>Add</button>;
}默认使用服务器组件。仅在需要以下功能时添加'use client':
- 事件处理器(onClick、onChange)
- 状态(useState、useReducer)
- 副作用(useEffect)
- 浏览器API
tsx
// 服务器组件(默认)- 无'use client'
async function ProductPage({ params }) {
const product = await getProduct(params.id); // 服务端获取数据
return (
<div>
<h1>{product.name}</h1>
<AddToCartButton productId={product.id} /> {/* 客户端组件 */}
</div>
);
}
// 客户端组件
'use client';
function AddToCartButton({ productId }) {
const [adding, setAdding] = useState(false);
return <button onClick={() => addToCart(productId)}>加入购物车</button>;
}Image Optimization
图片优化
tsx
import Image from 'next/image';
// Above the fold - load immediately
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority
/>
// Responsive image with fill
<div className="relative aspect-video">
<Image
src="/product.jpg"
alt="Product"
fill
sizes="(max-width: 768px) 100vw, 50vw"
className="object-cover"
/>
</div>tsx
import Image from 'next/image';
// 首屏图片 - 立即加载
<Image
src="/hero.jpg"
alt="首屏图"
width={1200}
height={600}
priority
/>
// 响应式填充图片
<div className="relative aspect-video">
<Image
src="/product.jpg"
alt="产品图"
fill
sizes="(max-width: 768px) 100vw, 50vw"
className="object-cover"
/>
</div>Data Fetching Patterns
数据获取模式
tsx
// Parallel fetching
async function Dashboard() {
const [user, stats] = await Promise.all([
getUser(),
getStats()
]);
return <div>...</div>;
}
// Streaming with Suspense
async function ProductPage({ params }) {
return (
<div>
<ProductDetails id={params.id} />
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews productId={params.id} />
</Suspense>
</div>
);
}tsx
// 并行获取数据
async function Dashboard() {
const [user, stats] = await Promise.all([
getUser(),
getStats()
]);
return <div>...</div>;
}
// 使用Suspense流式加载
async function ProductPage({ params }) {
return (
<div>
<ProductDetails id={params.id} />
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews productId={params.id} />
</Suspense>
</div>
);
}Accessibility and Testing
无障碍与测试
Reference:
references/frontend_best_practices.md参考:
references/frontend_best_practices.mdAccessibility Checklist
无障碍检查清单
- Semantic HTML: Use proper elements (,
<button>,<nav>)<main> - Keyboard Navigation: All interactive elements focusable
- ARIA Labels: Provide labels for icons and complex widgets
- Color Contrast: Minimum 4.5:1 for normal text
- Focus Indicators: Visible focus states
tsx
// Accessible button
<button
type="button"
aria-label="Close dialog"
onClick={onClose}
className="focus-visible:ring-2 focus-visible:ring-blue-500"
>
<XIcon aria-hidden="true" />
</button>
// Skip link for keyboard users
<a href="#main-content" className="sr-only focus:not-sr-only">
Skip to main content
</a>- 语义化HTML:使用合适的元素(、
<button>、<nav>)<main> - 键盘导航:所有交互元素可聚焦
- ARIA标签:为图标和复杂组件提供标签
- 颜色对比度:普通文本最低4.5:1
- 焦点指示器:可见的焦点状态
tsx
// 无障碍按钮
<button
type="button"
aria-label="关闭对话框"
onClick={onClose}
className="focus-visible:ring-2 focus-visible:ring-blue-500"
>
<XIcon aria-hidden="true" />
</button>
// 为键盘用户提供跳转链接
<a href="#main-content" className="sr-only focus:not-sr-only">
跳转到主要内容
</a>Testing Strategy
测试策略
tsx
// Component test with React Testing Library
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('button triggers action on click', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
// Test accessibility
test('dialog is accessible', async () => {
render(<Dialog open={true} title="Confirm" />);
expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(screen.getByRole('dialog')).toHaveAttribute('aria-labelledby');
});tsx
// 使用React Testing Library进行组件测试
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('按钮点击时触发操作', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>点击我</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
// 无障碍测试
test('对话框符合无障碍标准', async () => {
render(<Dialog open={true} title="确认" />);
expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(screen.getByRole('dialog')).toHaveAttribute('aria-labelledby');
});Quick Reference
快速参考
Common Next.js Config
常见Next.js配置
js
// next.config.js
const nextConfig = {
images: {
remotePatterns: [{ hostname: 'cdn.example.com' }],
formats: ['image/avif', 'image/webp'],
},
experimental: {
optimizePackageImports: ['lucide-react', '@heroicons/react'],
},
};js
// next.config.js
const nextConfig = {
images: {
remotePatterns: [{ hostname: 'cdn.example.com' }],
formats: ['image/avif', 'image/webp'],
},
experimental: {
optimizePackageImports: ['lucide-react', '@heroicons/react'],
},
};Tailwind CSS Utilities
Tailwind CSS工具类
tsx
// Conditional classes with cn()
import { cn } from '@/lib/utils';
<button className={cn(
'px-4 py-2 rounded',
variant === 'primary' && 'bg-blue-500 text-white',
disabled && 'opacity-50 cursor-not-allowed'
)} />tsx
// 使用cn()实现条件类名
import { cn } from '@/lib/utils';
<button className={cn(
'px-4 py-2 rounded',
variant === 'primary' && 'bg-blue-500 text-white',
disabled && 'opacity-50 cursor-not-allowed'
)} />TypeScript Patterns
TypeScript模式
tsx
// Props with children
interface CardProps {
className?: string;
children: React.ReactNode;
}
// Generic component
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}tsx
// 包含子元素的Props
interface CardProps {
className?: string;
children: React.ReactNode;
}
// 泛型组件
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}Resources
资源
- React Patterns:
references/react_patterns.md - Next.js Optimization:
references/nextjs_optimization_guide.md - Best Practices:
references/frontend_best_practices.md
- React模式:
references/react_patterns.md - Next.js优化:
references/nextjs_optimization_guide.md - 最佳实践:
references/frontend_best_practices.md