高级前端
前端开发模式、性能优化和React/Next.js应用的自动化工具。
目录
项目搭建
使用TypeScript、Tailwind CSS和最佳实践配置生成新的Next.js或React项目。
工作流程:创建新前端项目
-
使用项目名称和模板运行搭建工具:
python scripts/frontend_scaffolder.py 我的应用 --template nextjs -
添加可选功能(认证、API、表单、测试、storybook):
python scripts/frontend_scaffolder.py 仪表盘 --template nextjs --features auth,api -
导航到项目并安装依赖:
cd 我的应用 && npm install -
启动开发服务器:
npm run dev
搭建选项
| 选项 | 描述 |
|---|---|
--template nextjs |
Next.js 14+ 带App Router和Server Components |
--template react |
React + Vite带TypeScript |
--features auth |
添加NextAuth.js认证 |
--features api |
添加React Query + API客户端 |
--features forms |
添加React Hook Form + Zod验证 |
--features testing |
添加Vitest + 测试库 |
--dry-run |
预览文件而不创建它们 |
生成结构(Next.js)
我的应用/
├── 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.json
组件生成
生成带TypeScript、测试和Storybook故事的React组件。
工作流程:创建新组件
-
生成一个客户端组件:
python scripts/component_generator.py 按钮 --dir src/components/ui -
生成一个服务器组件:
python scripts/component_generator.py 商品卡片 --type server -
生成带测试和故事文件:
python scripts/component_generator.py 用户档案 --with-test --with-story -
生成一个自定义钩子:
python scripts/component_generator.py 表单验证 --type hook
生成器选项
| 选项 | 描述 |
|---|---|
--type client |
客户端组件带’use client’(默认) |
--type server |
异步服务器组件 |
--type hook |
自定义React钩子 |
--with-test |
包含测试文件 |
--with-story |
包含Storybook故事 |
--flat |
在输出目录中创建,不带子目录 |
--dry-run |
预览而不创建文件 |
生成组件示例
'use client';
import { useState } from 'react';
import { cn } from '@/lib/utils';
interface 按钮Props {
className?: string;
children?: React.ReactNode;
}
export function 按钮({ className, children }: 按钮Props) {
return (
<div className={cn('', className)}>
{children}
</div>
);
}
包分析
分析package.json和项目结构,寻找包优化机会。
工作流程:优化包大小
-
在项目上运行分析器:
python scripts/bundle_analyzer.py /path/to/project -
审查健康分数和问题:
包健康分数:75/100 (C) 重依赖: moment (290KB) 替代品:date-fns (12KB) 或 dayjs (2KB) lodash (71KB) 替代品:带tree-shaking的lodash-es -
应用推荐的修复,替换重依赖。
-
以详细模式重新运行,检查导入模式:
python scripts/bundle_analyzer.py . --verbose
包分数解释
| 分数 | 等级 | 行动 |
|---|---|---|
| 90-100 | A | 包已经优化得很好 |
| 80-89 | B | 有小的优化空间 |
| 70-79 | C | 替换重依赖 |
| 60-69 | D | 多个问题需要关注 |
| 0-59 | F | 严重的包大小问题 |
检测到的重依赖
分析器识别这些常见的重包:
| 包 | 大小 | 替代品 |
|---|---|---|
| moment | 290KB | date-fns (12KB) 或 dayjs (2KB) |
| lodash | 71KB | 带tree-shaking的lodash-es |
| axios | 14KB | 本地fetch或ky (3KB) |
| jquery | 87KB | 本地DOM API |
| @mui/material | 很大 | shadcn/ui或Radix UI |
React模式
参考:references/react_patterns.md
复合组件
在相关组件之间共享状态:
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>
自定义钩子
提取可重用的逻辑:
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);
渲染属性
共享渲染逻辑:
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优化
参考:references/nextjs_optimization_guide.md
服务器与客户端组件
默认使用服务器组件。当你需要时添加’use client’:
- 事件处理器(onClick, onChange)
- 状态(useState, useReducer)
- 效果(useEffect)
- 浏览器API
// 服务器组件(默认)-没有'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>;
}
图像优化
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>
数据获取模式
// 并行获取
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>
);
}
可访问性和测试
参考:references/frontend_best_practices.md
可访问性清单
- 语义HTML:使用适当的元素(
<button>,<nav>,<main>) - 键盘导航:所有可交互元素可聚焦
- ARIA标签:为图标和复杂组件提供标签
- 颜色对比:正常文本最小4.5:1
- 焦点指示器:可见的焦点状态
// 可访问按钮
<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>
测试策略
// 组件测试与React Testing Library
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('按钮点击触发动作', async () => {
const onClick = vi.fn();
render(<按钮 onClick={onClick}>点击我</按钮>);
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');
});
快速参考
常见的Next.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工具类
// 使用cn()有条件地添加类
import { cn } from '@/lib/utils';
<button className={cn(
'px-4 py-2 rounded',
变体 === 'primary' && 'bg-blue-500 text-white',
禁用 && 'opacity-50 cursor-not-allowed'
)} />
TypeScript模式
// 带children的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>;
}
资源
- React模式:
references/react_patterns.md - Next.js优化:
references/nextjs_optimization_guide.md - 最佳实践:
references/frontend_best_practices.md