mcpField
F
frontend-dev-guidelines

frontend-dev-guidelines

github.com/bbeierle12开发9104 次安装1 天前更新
代码仓库

关于 frontend-dev-guidelines

frontend-dev-guidelines 是面向前端开发的实践指南,适用于使用 TypeScript 和 React 编写应用的开发者。内容涵盖组件组织、状态管理、性能优化、错误处理与测试。

主要功能

  1. 01按功能组织项目结构
  2. 02提供 TypeScript 组件模式
  3. 03说明本地与服务端状态管理
  4. 04涵盖样式和性能优化
  5. 05包含类型、错误处理与测试建议

适用场景

  1. 01规划前端项目目录与组件
  2. 02选择应用状态管理方式
  3. 03优化路由和组件加载
  4. 04编写组件及 Hook 测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bbeierle12/skill-mcp-claude --skill frontend-dev-guidelines

安装后在 Claude Code 中输入/frontend-dev-guidelines确认已加载。

附带文件_meta.json

详细介绍

前端开发指南

项目结构

src/
├── features/           # Feature-based organization
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── api/
│   │   └── index.ts
│   ├── dashboard/
│   └── settings/
├── components/         # Shared/common components
│   ├── ui/            # Base UI components
│   └── layout/        # Layout components
├── hooks/             # Shared hooks
├── utils/             # Utility functions
├── types/             # TypeScript types
├── api/               # API layer
└── styles/            # Global styles

组件模式

使用 TypeScript 的函数组件
interface Props {
  title: string;
  onAction: (id: string) => void;
  isLoading?: boolean;
}

export function MyComponent({ title, onAction, isLoading = false }: Props) {
  // Component logic
  return (
    <div>
      {isLoading ? <Spinner /> : <Content title={title} />}
    </div>
  );
}
使用 Suspense 获取数据
import { Suspense } from 'react';

function ParentComponent() {
  return (
    <Suspense fallback={<LoadingSkeleton />}>
      <DataComponent />
    </Suspense>
  );
}

function DataComponent() {
  // useSuspenseQuery automatically suspends
  const { data } = useSuspenseQuery({
    queryKey: ['items'],
    queryFn: fetchItems,
  });
  
  return <ItemList items={data} />;
}
路由懒加载
import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./features/dashboard'));
const Settings = lazy(() => import('./features/settings'));

function App() {
  return (
    <Suspense fallback={<PageLoader />}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

状态管理

本地状态
const [value, setValue] = useState<string>('');
const [items, setItems] = useState<Item[]>([]);
服务端状态(TanStack Query)
// Queries
const { data, isLoading, error } = useQuery({
  queryKey: ['users', userId],
  queryFn: () => fetchUser(userId),
});

// Mutations
const mutation = useMutation({
  mutationFn: updateUser,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['users'] });
  },
});
全局状态(Zustand 或 Context)
// Simple global state with Zustand
const useStore = create<Store>((set) => ({
  theme: 'light',
  setTheme: (theme) => set({ theme }),
}));

样式最佳实践

Tailwind CSS
<div className="flex items-center gap-4 p-4 bg-white rounded-lg shadow-md">
  <span className="text-lg font-semibold text-gray-900">
    {title}
  </span>
</div>
CSS Modules(必要时)
import styles from './Component.module.css';

<div className={styles.container}>
  <span className={styles.title}>{title}</span>
</div>
Styled Components / Emotion(如有使用)
const Container = styled.div`
  display: flex;
  align-items: center;
  padding: ${({ theme }) => theme.spacing.md};
`;

性能优化

记忆化
// Memoize expensive computations
const expensiveValue = useMemo(() => {
  return computeExpensiveValue(data);
}, [data]);

// Memoize callbacks
const handleClick = useCallback((id: string) => {
  onAction(id);
}, [onAction]);

// Memoize components
const MemoizedComponent = memo(function Component({ data }: Props) {
  return <div>{data.value}</div>;
});
代码拆分
// Route-based splitting
const Page = lazy(() => import('./Page'));

// Component-based splitting
const HeavyComponent = lazy(() => import('./HeavyComponent'));
虚拟列表
import { useVirtualizer } from '@tanstack/react-virtual';

function VirtualList({ items }: { items: Item[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 50,
  });
  
  return (
    <div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
      <div style={{ height: virtualizer.getTotalSize() }}>
        {virtualizer.getVirtualItems().map((virtualItem) => (
          <div key={virtualItem.key} style={{
            position: 'absolute',
            top: virtualItem.start,
            height: virtualItem.size,
          }}>
            {items[virtualItem.index].name}
          </div>
        ))}
      </div>
    </div>
  );
}

TypeScript 最佳实践

类型定义
// Props interfaces
interface ButtonProps {
  variant: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
  onClick: () => void;
  children: React.ReactNode;
}

// API response types
interface ApiResponse<T> {
  data: T;
  meta: {
    page: number;
    total: number;
  };
}

// Utility types
type PartialUser = Partial<User>;
type RequiredUser = Required<User>;
type UserKeys = keyof User;
严格空值检查
// Handle potentially null values
const user = useUser();

if (!user) {
  return <NotFound />;
}

// Now TypeScript knows user is defined
return <UserProfile user={user} />;

错误处理

错误边界
import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

<ErrorBoundary FallbackComponent={ErrorFallback}>
  <MyComponent />
</ErrorBoundary>

测试

组件测试
import { render, screen, fireEvent } from '@testing-library/react';

describe('Button', () => {
  it('calls onClick when clicked', () => {
    const onClick = vi.fn();
    render(<Button onClick={onClick}>Click me</Button>);
    
    fireEvent.click(screen.getByRole('button'));
    
    expect(onClick).toHaveBeenCalledOnce();
  });
});
Hook 测试
import { renderHook, act } from '@testing-library/react';

describe('useCounter', () => {
  it('increments count', () => {
    const { result } = renderHook(() => useCounter());
    
    act(() => {
      result.current.increment();
    });
    
    expect(result.current.count).toBe(1);
  });
});

资源文件

详细模式请参阅:

评论

支持 Markdown