mcpField
F
Frontend Builder

Frontend Builder

github.com/daffy0208开发38215 次安装44 周前更新
代码仓库

关于 Frontend Builder

Frontend Builder 是面向 React 和 Next.js 前端开发者的技能指南,提供组件、状态、数据获取与性能优化方面的实践建议。内容也涵盖框架选择和表单处理。

主要功能

  1. 01React 与 Next.js 选型建议
  2. 02可复用组件设计
  3. 03状态管理决策指南
  4. 04数据获取与表单处理
  5. 05渲染与资源优化

适用场景

  1. 01规划新前端项目的框架
  2. 02拆分可维护的 UI 组件
  3. 03为应用选择状态管理方式
  4. 04改进前端加载与渲染性能

安装

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

$npx skills add https://github.com/daffy0208/ai-dev-standards --skill frontend-builder

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

附带文件manifest.yamlREADME.md

详细介绍

前端构建器

构建易于维护、性能出色的 React 和 Next.js 前端。

核心原则

1. 组件组合

将 UI 拆分为小型、可复用、职责单一的组件

2. 状态就近

尽可能将状态放在使用它的位置附近

3. 默认注重性能

优化渲染、代码拆分和资源加载

4. 开发者体验

命名清晰、模式一致、错误信息有帮助

框架选择

React (Vite) 与 Next.js

在以下情况下使用 React + Vite:

  • 仅限客户端的应用
  • 无 SEO 要求
  • 部署简单(静态托管)
  • 初始设置更快

在以下情况下使用 Next.js:

  • SEO 很重要(营销网站、博客、电商)
  • 需要服务器端渲染
  • 需要 API 路由
  • 更倾向于基于文件的路由
  • 图像优化至关重要

大多数项目的推荐选择:Next.js(App Router)


组件架构

组件类型

1. 页面组件(路由入口):

// app/users/page.tsx (Next.js App Router)
export default function UsersPage() {
  return (
    <div>
      <Header />
      <UserList />
      <Footer />
    </div>
  )
}

2. 功能组件(业务逻辑):

// components/features/UserList.tsx
export function UserList() {
  const { data, isLoading } = useUsers()

  if (isLoading) return <LoadingSpinner />

  return (
    <div>
      {data.map(user => <UserCard key={user.id} user={user} />)}
    </div>
  )
}

3. UI 组件(可复用,无业务逻辑):

// components/ui/button.tsx
export function Button({ children, variant = 'primary', ...props }) {
  return (
    <button
      className={cn(buttonVariants[variant])}
      {...props}
    >
      {children}
    </button>
  )
}
组件最佳实践
// ✅ Good: Small, focused, typed
interface UserProfileProps {
  user: User
  onEdit?: () => void
}

export function UserProfile({ user, onEdit }: UserProfileProps) {
  return (
    <div className="flex gap-4">
      <Avatar src={user.avatar} alt={user.name} />
      <UserDetails user={user} />
      {onEdit && <Button onClick={onEdit}>Edit</Button>}
    </div>
  )
}

// ❌ Bad: Giant, untyped, unclear
export function UserProfile(props) {
  // 500 lines of JSX, multiple responsibilities
  return <div>...</div>
}

状态管理

决策树
How many components need this state?
│
├─ One component → useState
├─ Parent + children → Props or useState + props
├─ Siblings → Lift to common parent
├─ Widely used (theme, auth) → Context API
└─ Complex app state → Zustand or Redux
本地状态(useState)
// For component-level state
function Counter() {
  const [count, setCount] = useState(0)
  const [isOpen, setIsOpen] = useState(false)

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </div>
  )
}
Context API
// For app-wide state (theme, auth, user)
const UserContext = createContext<UserContextType | undefined>(undefined)

export function UserProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null)

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  )
}

export function useUser() {
  const context = useContext(UserContext)
  if (!context) throw new Error('useUser must be within UserProvider')
  return context
}
Zustand(复杂状态的推荐选择)
import { create } from 'zustand'

interface CounterStore {
  count: number
  increment: () => void
  decrement: () => void
  reset: () => void
}

export const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 })
}))

// Usage
function Counter() {
  const { count, increment } = useCounterStore()
  return <button onClick={increment}>{count}</button>
}

数据获取

React Query(推荐)
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'

// Query (GET)
function Users() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    staleTime: 5 * 60 * 1000 // 5 minutes
  })

  if (isLoading) return <LoadingSpinner />
  if (error) return <ErrorMessage error={error} />

  return <UserList users={data} />
}

// Mutation (POST, PUT, DELETE)
function CreateUser() {
  const queryClient = useQueryClient()

  const mutation = useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] })
    }
  })

  return (
    <button onClick={() => mutation.mutate({ name: 'John' })}>
      Create User
    </button>
  )
}
Next.js Server Components(App Router)
// app/users/page.tsx
// Server Component - fetches on server
export default async function UsersPage() {
  const users = await fetchUsers() // Runs on server

  return <UserList users={users} />
}

// Client Component - for interactivity
'use client'

export function UserList({ users }: { users: User[] }) {
  const [selected, setSelected] = useState<string | null>(null)

  return (
    <div>
      {users.map(user => (
        <UserCard
          key={user.id}
          user={user}
          onClick={() => setSelected(user.id)}
        />
      ))}
    </div>
  )
}

表单处理

React Hook Form(推荐)
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const loginSchema = z.object({
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters')
})

type LoginForm = z.infer<typeof loginSchema>

function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting }
  } = useForm<LoginForm>({
    resolver: zodResolver(loginSchema)
  })

  const onSubmit = async (data: LoginForm) => {
    await login(data)
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <input
          {...register('email')}
          type="email"
          placeholder="Email"
          className="border p-2"
        />
        {errors.email && (
          <span className="text-red-500">{errors.email.message}</span>
        )}
      </div>

      <div>
        <input
          {...register('password')}
          type="password"
          placeholder="Password"
          className="border p-2"
        />
        {errors.password && (
          <span className="text-red-500">{errors.password.message}</span>
        )}
      </div>

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Logging in...' : 'Login'}
      </button>
    </form>
  )
}

样式

Tailwind CSS(推荐)
// Install: @shadcn/ui for component library
function Button({ variant = 'primary', children, ...props }) {
  return (
    <button
      className={cn(
        'px-4 py-2 rounded font-medium transition-colors',
        {
          'bg-blue-500 text-white hover:bg-blue-600': variant === 'primary',
          'bg-gray-200 text-gray-900 hover:bg-gray-300': variant === 'secondary',
          'bg-red-500 text-white hover:bg-red-600': variant === 'danger'
        }
      )}
      {...props}
    >
      {children}
    </button>
  )
}
CSS Modules(替代方案)
// Button.module.css
.button {
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
}

.primary {
  background-color: blue;
  color: white;
}

// Button.tsx
import styles from './Button.module.css'

export function Button({ variant = 'primary', children }) {
  return (
    <button className={`${styles.button} ${styles[variant]}`}>
      {children}
    </button>
  )
}

性能优化

React 优化
import { memo, useMemo, useCallback } from 'react'

// 1. Memoize expensive calculations
function DataTable({ data }) {
  const sortedData = useMemo(
    () => data.sort((a, b) => a.name.localeCompare(b.name)),
    [data]
  )

  return <Table data={sortedData} />
}

// 2. Memoize callbacks
function Parent() {
  const handleClick = useCallback(() => {
    console.log('Clicked')
  }, [])

  return <ExpensiveChild onClick={handleClick} />
}

// 3. Memoize components
const ExpensiveChild = memo(function ExpensiveChild({ onClick }) {
  return <button onClick={onClick}>Click</button>
})
Next.js 优化
// 1. Image optimization
import Image from 'next/image'

<Image
  src="/photo.jpg"
  alt="Photo"
  width={500}
  height={300}
  priority // Above the fold
/>

// 2. Font optimization
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export default function RootLayout({ children }) {
  return (
    <html className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

// 3. Dynamic imports (code splitting)
import dynamic from 'next/dynamic'

const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
  loading: () => <LoadingSpinner />
})

错误处理

错误边界
'use client'

import { Component, ReactNode } from 'react'

interface Props {
  children: ReactNode
  fallback?: ReactNode
}

interface State {
  hasError: boolean
  error?: Error
}

export class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props)
    this.state = { hasError: false }
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error }
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="p-4 bg-red-50 border border-red-200">
          <h2 className="text-red-800">Something went wrong</h2>
          <p className="text-red-600">{this.state.error?.message}</p>
        </div>
      )
    }

    return this.props.children
  }
}
Next.js 错误处理
// app/error.tsx
'use client'

export default function Error({
  error,
  reset
}: {
  error: Error
  reset: () => void
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={reset}>Try again</button>
    </div>
  )
}

文件夹结构

Next.js App Router
app/
├── (auth)/              # Route group (auth pages)
│   ├── login/
│   └── signup/
├── (dashboard)/         # Route group (dashboard)
│   ├── layout.tsx
│   ├── page.tsx
│   └── settings/
├── api/                 # API routes
│   └── users/
│       └── route.ts
└── layout.tsx           # Root layout

components/
├── ui/                  # shadcn/ui components
│   ├── button.tsx
│   ├── input.tsx
│   └── dialog.tsx
├── features/            # Feature components
│   ├── UserList.tsx
│   └── UserProfile.tsx
└── layouts/             # Layout components
    ├── Header.tsx
    └── Footer.tsx

lib/
├── utils.ts             # Utility functions
├── api.ts               # API client
└── validation.ts        # Zod schemas

hooks/
├── useUser.ts
└── useDebounce.ts

stores/
└── userStore.ts         # Zustand stores

TypeScript 最佳实践

// 1. Type component props
interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger'
  children: ReactNode
  onClick?: () => void
}

export function Button({ variant = 'primary', children, onClick }: ButtonProps) {
  return <button onClick={onClick}>{children}</button>
}

// 2. Type API responses
interface User {
  id: string
  name: string
  email: string
}

async function fetchUsers(): Promise<User[]> {
  const res = await fetch('/api/users')
  return res.json()
}

// 3. Type state
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState<boolean>(false)

总结

优秀的前端:

  • ✅ 大多数项目使用 Next.js(SEO、性能、开发体验)
  • ✅ 将 UI 拆分为小型、带类型的组件
  • ✅ 选择合适的状态管理方式(useState → Context → Zustand)
  • ✅ 使用 React Query 管理服务端状态
  • ✅ 使用 Tailwind CSS + shadcn/ui 编写样式
  • ✅ 通过记忆化和代码拆分进行优化
  • ✅ 使用错误边界妥善处理错误
  • ✅ 遵循一致的文件夹结构

相关资源

相关技能:

  • api-designer - 设计要使用的后端 API
  • ux-designer - 创建待实现的 UX 设计
  • deployment-advisor - 托管 Next.js/React 应用

相关模式:

  • META/DECISION-FRAMEWORK.md - 前端框架选择
  • STANDARDS/architecture-patterns/component-patterns.md - 组件设计模式(创建后提供)

相关操作手册:

  • PLAYBOOKS/setup-nextjs-project.md - Next.js 项目设置(创建后提供)
  • PLAYBOOKS/optimize-frontend-performance.md - 性能优化(创建后提供)

评论

支持 Markdown