senior-frontend
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSenior Frontend Developer
资深前端开发者
Expert-level frontend development for modern web applications.
为现代Web应用提供专家级前端开发服务。
Core Competencies
核心能力
- Component architecture
- State management
- Performance optimization
- Accessibility (a11y)
- CSS architecture
- Build optimization
- Testing strategies
- Design system implementation
- 组件架构
- 状态管理
- 性能优化
- 可访问性(a11y)
- CSS架构
- 构建优化
- 测试策略
- 设计系统落地
Framework Comparison
框架对比
| Feature | React | Vue | Svelte |
|---|---|---|---|
| Learning Curve | Medium | Low | Low |
| Bundle Size | Medium | Small | Smallest |
| Ecosystem | Largest | Large | Growing |
| Performance | Excellent | Excellent | Best |
| Enterprise Use | Highest | High | Medium |
| 特性 | React | Vue | Svelte |
|---|---|---|---|
| 学习曲线 | 中等 | 低 | 低 |
| 包体积 | 中等 | 小 | 最小 |
| 生态系统 | 最庞大 | 庞大 | 增长中 |
| 性能 | 优秀 | 优秀 | 最佳 |
| 企业级应用程度 | 最高 | 高 | 中等 |
React Patterns
React设计模式
Component Patterns
组件模式
Presentational Component:
typescript
interface ButtonProps {
variant: 'primary' | 'secondary';
size: 'sm' | 'md' | 'lg';
disabled?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export function Button({
variant,
size,
disabled,
children,
onClick
}: ButtonProps) {
return (
<button
className={cn(
'button',
`button--${variant}`,
`button--${size}`,
disabled && 'button--disabled'
)}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}Container Component:
typescript
export function UserListContainer() {
const { data: users, isLoading, error } = useUsers();
if (isLoading) return <Skeleton count={5} />;
if (error) return <ErrorMessage error={error} />;
return <UserList users={users} />;
}Higher-Order Component:
typescript
function withAuth<P extends object>(Component: React.ComponentType<P>) {
return function AuthenticatedComponent(props: P) {
const { user, isLoading } = useAuth();
if (isLoading) return <Loading />;
if (!user) return <Navigate to="/login" />;
return <Component {...props} />;
};
}Presentational Component:
typescript
interface ButtonProps {
variant: 'primary' | 'secondary';
size: 'sm' | 'md' | 'lg';
disabled?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export function Button({
variant,
size,
disabled,
children,
onClick
}: ButtonProps) {
return (
<button
className={cn(
'button',
`button--${variant}`,
`button--${size}`,
disabled && 'button--disabled'
)}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}Container Component:
typescript
export function UserListContainer() {
const { data: users, isLoading, error } = useUsers();
if (isLoading) return <Skeleton count={5} />;
if (error) return <ErrorMessage error={error} />;
return <UserList users={users} />;
}Higher-Order Component:
typescript
function withAuth<P extends object>(Component: React.ComponentType<P>) {
return function AuthenticatedComponent(props: P) {
const { user, isLoading } = useAuth();
if (isLoading) return <Loading />;
if (!user) return <Navigate to="/login" />;
return <Component {...props} />;
};
}Custom Hooks
自定义Hook
Data Fetching Hook:
typescript
function useApi<T>(url: string, options?: RequestInit) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
setIsLoading(true);
const response = await fetch(url, {
...options,
signal: controller.signal,
});
if (!response.ok) throw new Error('Request failed');
const json = await response.json();
setData(json);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err as Error);
}
} finally {
setIsLoading(false);
}
}
fetchData();
return () => controller.abort();
}, [url]);
return { data, error, isLoading };
}Local Storage Hook:
typescript
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window === 'undefined') return initialValue;
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
};
return [storedValue, setValue] as const;
}数据获取Hook:
typescript
function useApi<T>(url: string, options?: RequestInit) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
setIsLoading(true);
const response = await fetch(url, {
...options,
signal: controller.signal,
});
if (!response.ok) throw new Error('Request failed');
const json = await response.json();
setData(json);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err as Error);
}
} finally {
setIsLoading(false);
}
}
fetchData();
return () => controller.abort();
}, [url]);
return { data, error, isLoading };
}本地存储Hook:
typescript
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window === 'undefined') return initialValue;
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
};
return [storedValue, setValue] as const;
}State Management
状态管理
Zustand
Zustand
typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface CartStore {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
clearCart: () => void;
total: () => number;
}
export const useCartStore = create<CartStore>()(
persist(
(set, get) => ({
items: [],
addItem: (item) =>
set((state) => ({
items: [...state.items, item],
})),
removeItem: (id) =>
set((state) => ({
items: state.items.filter((item) => item.id !== id),
})),
clearCart: () => set({ items: [] }),
total: () =>
get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
}),
{ name: 'cart-storage' }
)
);typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface CartStore {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
clearCart: () => void;
total: () => number;
}
export const useCartStore = create<CartStore>()(
persist(
(set, get) => ({
items: [],
addItem: (item) =>
set((state) => ({
items: [...state.items, item],
})),
removeItem: (id) =>
set((state) => ({
items: state.items.filter((item) => item.id !== id),
})),
clearCart: () => set({ items: [] }),
total: () =>
get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
}),
{ name: 'cart-storage' }
)
);React Query
React Query
typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Query
function useProducts(category: string) {
return useQuery({
queryKey: ['products', category],
queryFn: () => fetchProducts(category),
staleTime: 5 * 60 * 1000,
});
}
// Mutation
function useCreateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createProduct,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['products'] });
},
});
}
// Optimistic Update
function useUpdateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateProduct,
onMutate: async (newProduct) => {
await queryClient.cancelQueries({ queryKey: ['products', newProduct.id] });
const previous = queryClient.getQueryData(['products', newProduct.id]);
queryClient.setQueryData(['products', newProduct.id], newProduct);
return { previous };
},
onError: (err, newProduct, context) => {
queryClient.setQueryData(['products', newProduct.id], context?.previous);
},
onSettled: (data, error, variables) => {
queryClient.invalidateQueries({ queryKey: ['products', variables.id] });
},
});
}typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Query
function useProducts(category: string) {
return useQuery({
queryKey: ['products', category],
queryFn: () => fetchProducts(category),
staleTime: 5 * 60 * 1000,
});
}
// Mutation
function useCreateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createProduct,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['products'] });
},
});
}
// Optimistic Update
function useUpdateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateProduct,
onMutate: async (newProduct) => {
await queryClient.cancelQueries({ queryKey: ['products', newProduct.id] });
const previous = queryClient.getQueryData(['products', newProduct.id]);
queryClient.setQueryData(['products', newProduct.id], newProduct);
return { previous };
},
onError: (err, newProduct, context) => {
queryClient.setQueryData(['products', newProduct.id], context?.previous);
},
onSettled: (data, error, variables) => {
queryClient.invalidateQueries({ queryKey: ['products', variables.id] });
},
});
}CSS Architecture
CSS架构
Tailwind CSS Best Practices
Tailwind CSS最佳实践
Component Classes:
typescript
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
outline: 'border border-gray-300 hover:bg-gray-100',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-base',
lg: 'h-12 px-6 text-lg',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);组件类:
typescript
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
outline: 'border border-gray-300 hover:bg-gray-100',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-base',
lg: 'h-12 px-6 text-lg',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);CSS Modules
CSS Modules
css
/* Button.module.css */
.button {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.2s;
}
.primary {
background-color: var(--color-primary);
color: white;
}
.primary:hover {
background-color: var(--color-primary-dark);
}css
/* Button.module.css */
.button {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.2s;
}
.primary {
background-color: var(--color-primary);
color: white;
}
.primary:hover {
background-color: var(--color-primary-dark);
}Accessibility
可访问性
ARIA Patterns
ARIA模式
Modal Dialog:
typescript
function Modal({ isOpen, onClose, title, children }) {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isOpen) {
modalRef.current?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
ref={modalRef}
tabIndex={-1}
onKeyDown={(e) => e.key === 'Escape' && onClose()}
>
<h2 id="modal-title">{title}</h2>
{children}
<button onClick={onClose} aria-label="Close modal">
Close
</button>
</div>
);
}Tab Panel:
typescript
function Tabs({ tabs, activeTab, onChange }) {
return (
<div>
<div role="tablist" aria-label="Content tabs">
{tabs.map((tab, index) => (
<button
key={tab.id}
role="tab"
aria-selected={activeTab === index}
aria-controls={`panel-${tab.id}`}
id={`tab-${tab.id}`}
tabIndex={activeTab === index ? 0 : -1}
onClick={() => onChange(index)}
>
{tab.label}
</button>
))}
</div>
{tabs.map((tab, index) => (
<div
key={tab.id}
role="tabpanel"
id={`panel-${tab.id}`}
aria-labelledby={`tab-${tab.id}`}
hidden={activeTab !== index}
>
{tab.content}
</div>
))}
</div>
);
}模态对话框:
typescript
function Modal({ isOpen, onClose, title, children }) {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isOpen) {
modalRef.current?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
ref={modalRef}
tabIndex={-1}
onKeyDown={(e) => e.key === 'Escape' && onClose()}
>
<h2 id="modal-title">{title}</h2>
{children}
<button onClick={onClose} aria-label="Close modal">
Close
</button>
</div>
);
}标签页面板:
typescript
function Tabs({ tabs, activeTab, onChange }) {
return (
<div>
<div role="tablist" aria-label="Content tabs">
{tabs.map((tab, index) => (
<button
key={tab.id}
role="tab"
aria-selected={activeTab === index}
aria-controls={`panel-${tab.id}`}
id={`tab-${tab.id}`}
tabIndex={activeTab === index ? 0 : -1}
onClick={() => onChange(index)}
>
{tab.label}
</button>
))}
</div>
{tabs.map((tab, index) => (
<div
key={tab.id}
role="tabpanel"
id={`panel-${tab.id}`}
aria-labelledby={`tab-${tab.id}`}
hidden={activeTab !== index}
>
{tab.content}
</div>
))}
</div>
);
}Accessibility Checklist
可访问性检查清单
- Keyboard navigation works
- Focus indicators visible
- Color contrast meets WCAG AA
- Images have alt text
- Form inputs have labels
- Error messages are announced
- Skip links present
- Heading hierarchy correct
- ARIA labels for icons
- Motion respects user preferences
- 键盘导航正常工作
- 焦点指示器可见
- 颜色对比度符合WCAG AA标准
- 图片包含替代文本
- 表单输入项带有标签
- 错误消息可被屏幕阅读器读取
- 提供跳转链接
- 标题层级正确
- 图标带有ARIA标签
- 动画效果尊重用户偏好
Performance Optimization
性能优化
Code Splitting
代码分割
typescript
// Route-based splitting
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
// Component-based splitting
const HeavyChart = lazy(() => import('./components/HeavyChart'));
// Named exports
const { DataTable } = lazy(() =>
import('./components/DataTable').then((module) => ({
default: module.DataTable,
}))
);typescript
// 基于路由的分割
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
// 基于组件的分割
const HeavyChart = lazy(() => import('./components/HeavyChart'));
// 具名导出
const { DataTable } = lazy(() =>
import('./components/DataTable').then((module) => ({
default: module.DataTable,
}))
);Memoization
记忆化优化
typescript
// Memoize expensive component
const ExpensiveList = memo(function ExpensiveList({ items, onSelect }) {
return (
<ul>
{items.map((item) => (
<li key={item.id} onClick={() => onSelect(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
// Memoize callback
function Parent() {
const [selected, setSelected] = useState<string | null>(null);
const handleSelect = useCallback((id: string) => {
setSelected(id);
}, []);
return <ExpensiveList items={items} onSelect={handleSelect} />;
}
// Memoize computed value
function DataDisplay({ data }) {
const processedData = useMemo(() => {
return data.map(expensiveTransform).filter(expensiveFilter);
}, [data]);
return <Chart data={processedData} />;
}typescript
// 记忆化开销较大的组件
const ExpensiveList = memo(function ExpensiveList({ items, onSelect }) {
return (
<ul>
{items.map((item) => (
<li key={item.id} onClick={() => onSelect(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
// 记忆化回调函数
function Parent() {
const [selected, setSelected] = useState<string | null>(null);
const handleSelect = useCallback((id: string) => {
setSelected(id);
}, []);
return <ExpensiveList items={items} onSelect={handleSelect} />;
}
// 记忆化计算值
function DataDisplay({ data }) {
const processedData = useMemo(() => {
return data.map(expensiveTransform).filter(expensiveFilter);
}, [data]);
return <Chart data={processedData} />;
}Virtual Lists
虚拟列表
typescript
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
overscan: 5,
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div
style={{
height: `${virtualizer.getTotalSize()}px`,
position: 'relative',
}}
>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
transform: `translateY(${virtualItem.start}px)`,
height: `${virtualItem.size}px`,
}}
>
{items[virtualItem.index].name}
</div>
))}
</div>
</div>
);
}typescript
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
overscan: 5,
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div
style={{
height: `${virtualizer.getTotalSize()}px`,
position: 'relative',
}}
>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
transform: `translateY(${virtualItem.start}px)`,
height: `${virtualItem.size}px`,
}}
>
{items[virtualItem.index].name}
</div>
))}
</div>
</div>
);
}Testing
测试
Component Testing
组件测试
typescript
import { render, screen, fireEvent } from '@testing-library/react';
describe('Button', () => {
it('renders with correct text', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const onClick = jest.fn();
render(<Button onClick={onClick}>Click me</Button>);
fireEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});typescript
import { render, screen, fireEvent } from '@testing-library/react';
describe('Button', () => {
it('renders with correct text', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const onClick = jest.fn();
render(<Button onClick={onClick}>Click me</Button>);
fireEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});Hook Testing
Hook测试
typescript
import { renderHook, act } from '@testing-library/react';
describe('useCounter', () => {
it('increments counter', () => {
const { result } = renderHook(() => useCounter(0));
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});typescript
import { renderHook, act } from '@testing-library/react';
describe('useCounter', () => {
it('increments counter', () => {
const { result } = renderHook(() => useCounter(0));
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});Reference Materials
参考资料
- - Advanced React patterns
references/react_patterns.md - - CSS organization
references/css_architecture.md - - A11y guidelines
references/accessibility.md - - Performance optimization
references/performance.md
- - React高级设计模式
references/react_patterns.md - - CSS架构规范
references/css_architecture.md - - 可访问性指南
references/accessibility.md - - 性能优化手册
references/performance.md
Scripts
脚本
bash
undefinedbash
undefinedComponent scaffolder
组件脚手架
python scripts/component_gen.py --name Button --type atom
python scripts/component_gen.py --name Button --type atom
Bundle analyzer
包分析工具
python scripts/bundle_analyzer.py --build-dir ./dist
python scripts/bundle_analyzer.py --build-dir ./dist
Accessibility audit
可访问性审计
python scripts/a11y_audit.py --url http://localhost:3000
python scripts/a11y_audit.py --url http://localhost:3000
Performance report
性能报告
python scripts/perf_report.py --lighthouse-json report.json
undefinedpython scripts/perf_report.py --lighthouse-json report.json
undefined