senior-frontend
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSenior Frontend Engineer
高级前端工程师
Overview
概述
Deliver production-grade frontend code following a structured three-phase workflow: context discovery, development, and handoff. This skill enforces strict quality standards including atomic design component architecture, comprehensive state management patterns, SSR/SSG/ISR optimization, and mandatory >85% test coverage with Vitest, React Testing Library, and Playwright.
Announce at start: "I'm using the senior-frontend skill for production-grade React/TypeScript development."
交付遵循上下文调研、开发、交付三阶段结构化工作流的生产级前端代码。本技能严格执行质量标准,包括原子设计组件架构、全面的状态管理模式、SSR/SSG/ISR优化,以及使用Vitest、React Testing Library和Playwright实现强制>85%的测试覆盖率。
启动时声明: "我正在使用senior-frontend技能进行生产级React/TypeScript开发。"
Phase 1: Context Discovery
第一阶段:上下文调研
Goal: Understand the existing codebase before writing any code.
目标: 在编写任何代码前先理解现有代码库。
Actions
执行动作
- Analyze existing codebase structure and conventions
- Identify the tech stack version (React 18/19, Next.js 14/15, TypeScript version)
- Review existing component library and design system
- Check state management approach already in use
- Understand build tooling and CI pipeline
- Map existing test infrastructure and coverage
- 分析现有代码库结构和规范
- 确认技术栈版本(React 18/19、Next.js 14/15、TypeScript版本)
- 梳理现有组件库和设计系统
- 检查当前已在使用的状态管理方案
- 了解构建工具和CI流水线
- 梳理现有测试基础设施和覆盖率情况
STOP — Do NOT proceed to Phase 2 until:
停止 — 满足以下条件前不得进入第二阶段:
- Tech stack versions are identified
- Existing patterns and conventions are documented
- Test infrastructure is mapped
- State management approach is identified
- 已确认技术栈版本
- 已记录现有模式和规范
- 已梳理清楚测试基础设施
- 已确认状态管理方案
Phase 2: Development
第二阶段:开发
Goal: Implement with strict TypeScript, atomic design, and TDD.
目标: 遵循严格的TypeScript规范、原子设计原则和测试驱动开发(TDD)实现需求。
Actions
执行动作
- Design component architecture following atomic design
- Implement with TypeScript strict mode
- Write tests alongside implementation (TDD when appropriate)
- Optimize for performance (bundle size, rendering, loading)
- Ensure accessibility compliance
- 遵循原子设计原则设计组件架构
- 开启TypeScript严格模式进行开发
- 实现功能的同时编写测试(合适场景下使用TDD)
- 进行性能优化(包体积、渲染、加载速度)
- 确保符合无障碍访问规范
Component Architecture Decision Table (Atomic Design)
组件架构决策表(原子设计)
| Level | Description | Business Logic | Example |
|---|---|---|---|
| Atoms | Smallest building blocks | None | Button, Input, Icon, Badge |
| Molecules | Composed of atoms | Minimal | FormField, SearchBar, Card |
| Organisms | Complex with business logic | Yes | DataTable, NavigationBar, CommentThread |
| Templates | Page structure without data | Layout only | DashboardLayout, AuthLayout |
| Pages | Templates connected to data | Data fetching | UsersPage, SettingsPage |
| 层级 | 描述 | 业务逻辑 | 示例 |
|---|---|---|---|
| 原子(Atoms) | 最小的构建块 | 无 | Button、Input、Icon、Badge |
| 分子(Molecules) | 由原子组合而成 | 极简 | FormField、SearchBar、Card |
| 有机体(Organisms) | 包含业务逻辑的复杂组件 | 有 | DataTable、NavigationBar、CommentThread |
| 模板(Templates) | 不含数据的页面结构 | 仅布局 | DashboardLayout、AuthLayout |
| 页面(Pages) | 关联数据的模板 | 数据获取 | UsersPage、SettingsPage |
Atom Example
原子组件示例
typescript
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
}
export function Button({ variant = 'primary', size = 'md', isLoading, children, ...props }: ButtonProps) {
return (
<button className={cn(buttonVariants({ variant, size }))} disabled={isLoading || props.disabled} {...props}>
{isLoading ? <Spinner size={size} /> : children}
</button>
);
}typescript
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
}
export function Button({ variant = 'primary', size = 'md', isLoading, children, ...props }: ButtonProps) {
return (
<button className={cn(buttonVariants({ variant, size }))} disabled={isLoading || props.disabled} {...props}>
{isLoading ? <Spinner size={size} /> : children}
</button>
);
}State Management Decision Table
状态管理决策表
| State Type | Solution | When to Use |
|---|---|---|
| Server state | React Query / TanStack Query | API data, caching, sync |
| Form state | React Hook Form + Zod | Form validation, submission |
| Global UI state | Zustand | Theme, sidebar open, modals |
| Local UI state | useState / useReducer | Component-specific state |
| URL state | nuqs / useSearchParams | Filters, pagination, tabs |
| Complex local | useReducer | Multiple related state transitions |
| Shared context | React Context | Theme, locale, auth (infrequent updates) |
| 状态类型 | 解决方案 | 使用场景 |
|---|---|---|
| 服务端状态 | React Query / TanStack Query | API数据、缓存、同步 |
| 表单状态 | React Hook Form + Zod | 表单校验、提交 |
| 全局UI状态 | Zustand | 主题、侧边栏开关、弹窗 |
| 本地UI状态 | useState / useReducer | 组件专属状态 |
| URL状态 | nuqs / useSearchParams | 筛选、分页、标签页 |
| 复杂本地状态 | useReducer | 多个关联状态转换 |
| 共享上下文 | React Context | 主题、国际化、鉴权(低频率更新场景) |
SSR / SSG / ISR Decision Table (Next.js App Router)
SSR / SSG / ISR 决策表(Next.js App Router)
| Pattern | Use When | Cache Strategy |
|---|---|---|
| Static (SSG) | Content rarely changes | Build time |
| ISR | Content changes periodically | Revalidate interval |
| SSR | Content changes per request | No cache |
| Client | User-specific, interactive | Browser |
| 模式 | 使用场景 | 缓存策略 |
|---|---|---|
| 静态生成(SSG) | 内容极少变更 | 构建时生成 |
| 增量静态再生成(ISR) | 内容定期更新 | 按重验证间隔更新 |
| 服务端渲染(SSR) | 内容随每次请求变化 | 不缓存 |
| 客户端渲染 | 用户专属、高交互场景 | 浏览器端渲染 |
Server vs Client Component Decision
服务端组件 vs 客户端组件决策
| Need | Component Type |
|---|---|
| Direct data fetching | Server (default) |
| Event handlers (onClick, onChange) | Client ( |
| useState / useReducer | Client |
| useEffect / useLayoutEffect | Client |
| Browser APIs (window, localStorage) | Client |
| Third-party libs using client features | Client |
| No interactivity needed | Server (default) |
| 需求 | 组件类型 |
|---|---|
| 直接获取数据 | 服务端组件(默认) |
| 事件处理(onClick、onChange) | 客户端组件(添加 |
| 使用useState / useReducer | 客户端组件 |
| 使用useEffect / useLayoutEffect | 客户端组件 |
| 使用浏览器API(window、localStorage) | 客户端组件 |
| 依赖客户端特性的第三方库 | 客户端组件 |
| 无需交互 | 服务端组件(默认) |
STOP — Do NOT proceed to Phase 3 until:
停止 — 满足以下条件前不得进入第三阶段:
- Components follow atomic design hierarchy
- TypeScript strict mode is enabled, no types
any - Tests are written for all components
- Accessibility is verified (axe-core)
- 组件符合原子设计层级规范
- 已开启TypeScript严格模式,无类型
any - 所有组件都已编写测试
- 已通过无障碍访问校验(axe-core)
Phase 3: Handoff
第三阶段:交付
Goal: Verify quality gates and prepare for review.
目标: 验证质量门禁,准备代码评审。
Actions
执行动作
- Verify test coverage meets >85% threshold
- Run full lint and type check
- Document complex components with JSDoc/TSDoc
- Create Storybook stories for UI components
- Performance audit (Lighthouse, bundle analysis)
- 验证测试覆盖率达到>85%的阈值
- 运行完整的代码检查和类型校验
- 使用JSDoc/TSDoc为复杂组件编写文档
- 为UI组件创建Storybook stories
- 性能审计(Lighthouse、包体积分析)
Performance Checklist
性能检查清单
- Bundle size < 200KB gzipped (initial load)
- Largest Contentful Paint < 2.5s
- First Input Delay < 100ms
- Cumulative Layout Shift < 0.1
- Images: next/image with proper sizing and formats
- Fonts: next/font with display swap
- No layout thrashing (batch DOM reads/writes)
- Virtualization for lists > 100 items
- gzip压缩后初始加载包体积 < 200KB
- 最大内容绘制(LCP) < 2.5s
- 首次输入延迟(FID) < 100ms
- 累积布局偏移(CLS) < 0.1
- 图片:使用next/image,配置正确的尺寸和格式
- 字体:使用next/font,配置display swap
- 无布局抖动(批量处理DOM读写)
- 超过100项的列表使用虚拟滚动
Coverage Thresholds
覆盖率阈值配置
json
{
"coverageThreshold": {
"global": {
"branches": 85,
"functions": 85,
"lines": 85,
"statements": 85
}
}
}json
{
"coverageThreshold": {
"global": {
"branches": 85,
"functions": 85,
"lines": 85,
"statements": 85
}
}
}STOP — Handoff complete when:
停止 — 满足以下条件则交付完成:
- Test coverage >85% verified
- Lint and type check pass with zero errors
- Performance audit completed
- Complex components documented
- 已验证测试覆盖率>85%
- 代码检查和类型校验零错误通过
- 已完成性能审计
- 复杂组件已编写文档
Testing Requirements
测试要求
Unit Tests (Vitest + React Testing Library)
单元测试(Vitest + React Testing Library)
typescript
describe('Button', () => {
it('renders children', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
});
it('shows loading state', () => {
render(<Button isLoading>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
it('calls onClick when clicked', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledOnce();
});
});typescript
describe('Button', () => {
it('renders children', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
});
it('shows loading state', () => {
render(<Button isLoading>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
it('calls onClick when clicked', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledOnce();
});
});Integration Tests
集成测试
- Component compositions (form submission flow)
- Data fetching with MSW (Mock Service Worker)
- Routing and navigation
- Error boundaries and fallbacks
- 组件组合测试(表单提交流程)
- 使用MSW(Mock Service Worker)模拟数据获取
- 路由和导航测试
- 错误边界和降级方案测试
E2E Tests (Playwright)
E2E测试(Playwright)
typescript
test('user can complete checkout', async ({ page }) => {
await page.goto('/products');
await page.getByRole('button', { name: 'Add to cart' }).first().click();
await page.getByRole('link', { name: 'Cart' }).click();
await expect(page.getByText('1 item')).toBeVisible();
await page.getByRole('button', { name: 'Checkout' }).click();
});typescript
test('user can complete checkout', async ({ page }) => {
await page.goto('/products');
await page.getByRole('button', { name: 'Add to cart' }).first().click();
await page.getByRole('link', { name: 'Cart' }).click();
await expect(page.getByText('1 item')).toBeVisible();
await page.getByRole('button', { name: 'Checkout' }).click();
});React Query Patterns
React Query 模式
typescript
function useUsers(filters: UserFilters) {
return useQuery({
queryKey: ['users', filters],
queryFn: () => fetchUsers(filters),
staleTime: 5 * 60 * 1000,
placeholderData: keepPreviousData,
});
}
function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
await queryClient.cancelQueries({ queryKey: ['users'] });
const previous = queryClient.getQueryData(['users']);
queryClient.setQueryData(['users'], (old) =>
old.map(u => u.id === newUser.id ? { ...u, ...newUser } : u)
);
return { previous };
},
onError: (err, newUser, context) => {
queryClient.setQueryData(['users'], context.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
}typescript
function useUsers(filters: UserFilters) {
return useQuery({
queryKey: ['users', filters],
queryFn: () => fetchUsers(filters),
staleTime: 5 * 60 * 1000,
placeholderData: keepPreviousData,
});
}
function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
await queryClient.cancelQueries({ queryKey: ['users'] });
const previous = queryClient.getQueryData(['users']);
queryClient.setQueryData(['users'], (old) =>
old.map(u => u.id === newUser.id ? { ...u, ...newUser } : u)
);
return { previous };
},
onError: (err, newUser, context) => {
queryClient.setQueryData(['users'], context.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
}Memoization Decision Table
缓存决策表
| Technique | Use When | Do NOT Use When |
|---|---|---|
| Expensive computation, referential equality for deps | Simple calculations, primitive values |
| Functions passed to memoized children | Functions not passed as props |
| Component re-renders often with same props | Props change on every render |
| None | Default — do not memoize | Always profile first |
| 技术方案 | 使用场景 | 禁止使用场景 |
|---|---|---|
| 计算成本高的逻辑、依赖需要引用相等的场景 | 简单计算、原始值类型 |
| 传递给缓存子组件的函数 | 不作为props传递的函数 |
| 相同props下频繁重渲染的组件 | 每次渲染props都会变化的组件 |
| 无 | 默认场景 — 不要过度缓存 | 永远先做性能分析再决定是否缓存 |
Anti-Patterns / Common Mistakes
反模式 / 常见错误
| Anti-Pattern | Why It Is Wrong | Correct Approach |
|---|---|---|
| Race conditions, no caching, no dedup | React Query or Server Components |
| Prop drilling more than 2 levels | Tight coupling, maintenance burden | Composition, context, or Zustand |
| Business logic in components | Untestable, unreusable | Extract to hooks or utility functions |
| Barrel exports | Breaks tree-shaking, slower builds | Direct imports |
| Testing implementation details | Brittle tests that break on refactor | Test behavior: user actions and outcomes |
| Defeats TypeScript's purpose | |
| Inline styles for non-dynamic values | Inconsistent, hard to maintain | CSS modules, Tailwind, or styled-components |
| Memoizing everything | Adds complexity, often slower | Profile first, memoize second |
| 反模式 | 错误原因 | 正确做法 |
|---|---|---|
用 | 竞态条件、无缓存、无重复请求去重 | 使用React Query或服务端组件 |
| 属性透传超过2层 | 耦合度高、维护成本高 | 使用组件组合、上下文或者Zustand |
| 业务逻辑写在组件内 | 不可测试、不可复用 | 抽离到hooks或者工具函数中 |
| 桶导出(Barrel exports) | 破坏tree-shaking、构建变慢 | 直接导入 |
| 测试实现细节 | 测试脆弱,重构时容易失效 | 测试行为:用户操作和输出结果 |
任意位置使用 | 失去TypeScript的类型保护作用 | 使用 |
| 非动态值使用内联样式 | 不一致、难维护 | 使用CSS modules、Tailwind或者styled-components |
| 所有内容都加缓存 | 增加复杂度,往往反而更慢 | 先做性能分析,再按需缓存 |
Documentation Lookup (Context7)
文档查询(Context7)
Use then for up-to-date docs. Returned docs override memorized knowledge.
mcp__context7__resolve-library-idmcp__context7__query-docs- — when uncertain about hooks API, component lifecycle, or React 19+ features
react - — for App Router, Server Components, or Next.js-specific APIs
next.js - — for advanced type patterns or compiler options
typescript - — for utility classes, configuration, or plugin API
tailwindcss - — for test runner API, matchers, or mock utilities
vitest
先使用再使用获取最新文档,返回的文档优先级高于记忆中的知识。
mcp__context7__resolve-library-idmcp__context7__query-docs- — 不确定hooks API、组件生命周期或者React 19+新特性时查询
react - — 查询App Router、服务端组件或者Next.js专属API
next.js - — 查询高级类型模式或者编译器选项
typescript - — 查询工具类、配置或者插件API
tailwindcss - — 查询测试运行器API、断言方法或者mock工具
vitest
Integration Points
集成点
| Skill | Relationship |
|---|---|
| Strategy defines frontend test frameworks |
| Components are built with TDD cycle |
| Detailed React patterns complement this skill |
| Frontend performance follows optimization methodology |
| Review verifies component architecture and test coverage |
| Code quality principles apply to component code |
| Playwright E2E tests use this skill's page structure |
| UI acceptance criteria drive component tests |
| 技能 | 关联关系 |
|---|---|
| 测试策略定义前端测试框架选型 |
| 组件遵循TDD循环开发 |
| 详细的React模式是本技能的补充 |
| 前端性能遵循通用优化方法论 |
| 代码评审验证组件架构和测试覆盖率 |
| 组件代码遵循简洁代码质量原则 |
| Playwright E2E测试基于本技能的页面结构 |
| UI验收标准驱动组件测试编写 |
Key Principles
核心原则
- TypeScript strict mode, no (use
any+ type guards)unknown - Prefer composition over inheritance
- Colocate tests, styles, and stories with components
- Server Components by default; Client Components only when required
- Error boundaries at route and feature boundaries
- Accessibility is not optional (test with axe-core)
- 开启TypeScript严格模式,禁止使用(使用
any+ 类型守卫)unknown - 优先使用组合而非继承
- 测试、样式和stories与组件同位置存放
- 默认使用服务端组件;仅在必要时使用客户端组件
- 路由和功能边界设置错误边界
- 无障碍访问是强制要求(使用axe-core测试)
Skill Type
技能类型
FLEXIBLE — Adapt component architecture and state management to the existing project conventions. The three-phase workflow is strongly recommended. Test coverage must target >85%. TypeScript strict mode is non-negotiable.
灵活适配 — 可根据现有项目规范调整组件架构和状态管理方案。强烈推荐遵循三阶段工作流。测试覆盖率必须达标>85%。TypeScript严格模式为强制要求,不可妥协。