前端开发指南Skill frontend-dev-guidelines

Next.js 15 应用的前端开发指南,涵盖 React 19、TypeScript、Shadcn/ui、Tailwind CSS 等现代开发模式,包括 Server Components、Client Components、App Router、文件结构、组件、性能优化和 TypeScript 最佳实践。

前端开发 0 次安装 13 次浏览 更新于 2/28/2026

前端开发指南

目的

全面的指南,用于现代 Next.js 15 开发,强调 Server Components、Client Components、App Router 模式、Shadcn/ui 组件、适当的文件组织和性能优化。

何时使用此技能

  • 创建新组件或页面
  • 构建新功能
  • 获取数据(Server Components、Server Actions)
  • 使用 Next.js App Router 设置路由
  • 使用 Tailwind CSS 和 Shadcn/ui 组件进行样式设计
  • 性能优化
  • 组织前端代码
  • TypeScript 最佳实践

快速开始

新组件清单

创建组件?遵循此清单:

  • [ ] 确定 Server vs Client Component(默认:Server Component)
  • [ ] 仅在需要时添加 "use client" 指令(交互性、hooks、浏览器 API)
  • [ ] 使用 TypeScript 明确 prop 类型
  • [ ] 从 @/components/ui 导入 Shadcn/ui 组件
  • [ ] 使用 Tailwind CSS 类进行样式设计
  • [ ] 导入别名:@/components@/lib@/hooks
  • [ ] 使用 cn() 实用程序进行条件类
  • [ ] 默认导出在底部
  • [ ] 尽可能使用 Server Components 进行数据获取

新页面清单

创建页面?设置此结构:

  • [ ] 创建 app/{route-name}/page.tsx 用于路由
  • [ ] 默认使用 Server Component
  • [ ] 直接在 Server Component 中获取数据
  • [ ] 为页面特定组件创建 components/ 目录
  • [ ] 使用 loading.tsx 用于加载状态
  • [ ] 使用 error.tsx 用于错误边界
  • [ ] 导出元数据以进行 SEO

导入别名快速参考

别名 解析为 示例
@/ 项目根目录 import { cn } from '@/lib/utils'
@/components components/ import { Button } from '@/components/ui/button'
@/lib lib/ import { cn } from '@/lib/utils'
@/hooks hooks/ import { useMobile } from '@/hooks/use-mobile'
@/app app/ import { Metadata } from 'next'

定义在:tsconfig.json 路径配置


常用导入备忘单

// Next.js
import { Metadata } from 'next'
import { Suspense } from 'react'
import { notFound, redirect } from 'next/navigation'

// React (仅限 Client Components)
;('use client')
import { useState, useCallback, useMemo } from 'react'

// Shadcn/ui 组件
import { Button } from '@/components/ui/button'
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
import { Input } from '@/components/ui/input'

// 实用工具
import { cn } from '@/lib/utils'

// Hooks (仅限 Client Components)
import { useMobile } from '@/hooks/use-mobile'

// 类型
import type { ComponentProps } from 'react'

主题指南

🎨 组件模式

Server Components vs Client Components:

  • Server Components(默认):没有 "use client",可以直接获取数据,更小的捆绑包
  • Client Components:添加 "use client" 用于交互性、hooks、浏览器 API

关键概念:

  • 默认使用 Server Components
  • 仅在必要时使用 Client Components
  • 使用 Shadcn/ui 组件(已经是 Client Components)
  • 组件结构:Props → 数据获取 → 渲染 → 导出

示例 Server Component:

// app/features/posts/components/PostList.tsx
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'

接口 PostListProps {
  帖子:帖子[]
}

export 函数 PostList({ 帖子 }: PostListProps) {
  返回 (
    <div className='grid gap-4'>
      {帖子.map((帖子) => (
        <Card key={帖子.id}>
          <CardHeader>
            <CardTitle>{帖子.title}</CardTitle>
          </CardHeader>
          <CardContent>{帖子.content}</CardContent>
        </Card>
      ))}
    </div>
  )
}

示例 Client Component:

// app/features/posts/components/PostForm.tsx
'use client'

import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'

export 函数 PostForm() {
  常量 [标题,setTitle] = useState('')

  返回 (
    <form>
      <Input 值={标题} onChange={(e) => setTitle(e.target.value)} />
      <Button 类型='submit'>提交</Button>
    </form>
  )
}

📊 数据获取

主要模式:Server Components

  • 直接在 Server Components 中获取数据
  • 在 Server Components 中使用 async/await
  • 不需要 useEffect 或数据获取库
  • 自动请求去重

Server Actions:

  • 用于突变(表单、更新)
  • 创建 app/actions/ 目录
  • 标记为 "use server" 指令

示例 Server Component 与数据获取:

// app/posts/page.tsx
import { PostList } from '@/components/PostList'

异步 函数 getPosts() {
  常量 res = 等待 fetch('https://api.example.com/posts', {
    缓存:'no-store', // 或 'force-cache', 'revalidate'
  })
  返回 res.json()
}

export 默认 异步 函数 PostsPage() {
  常量 帖子 = 等待 getPosts()

  返回 <PostList 帖子={帖子} />
}

示例 Server Action:

// app/actions/posts.ts
'use server'

export 异步 函数 createPost(formData: FormData) {
  常量 标题 = formData.get('title')
  // ... 验证和创建逻辑
  重定向('/posts')
}

📁 文件组织

App Router 结构:

app/
  (routes)/
    页面.tsx          # 路由页面
    布局.tsx        # 路由布局
    加载.tsx       # 加载 UI
    错误.tsx         # 错误 UI
  components/         # 共享组件
    ui/               # Shadcn/ui 组件
  特性/           # 特定功能代码
    帖子/
      components/     # 功能组件
      actions/        # 服务器操作
      类型/          # TypeScript 类型
lib/
  utils.ts            # 实用工具(cn 等)
hooks/
  use-mobile.ts       # 自定义钩子(仅限客户端)

功能组织:

  • app/features/{feature}/:特定功能页面/路由
  • components/:真正可重用的组件
  • components/ui/:Shadcn/ui 组件(不要直接修改)

🎨 样式设计

Tailwind CSS + Shadcn/ui:

  • 使用 Tailwind 实用程序类
  • 使用 cn() 实用工具进行条件类
  • Shadcn/ui 组件使用 CSS 变量进行主题设置
  • app/globals.css 中自定义主题

样式设计模式:

import { cn } from '@/lib/utils'

接口 ButtonProps {
  变体?:'primary' | 'secondary'
  类名?:字符串
}

export 函数 Button({ 变体 = 'primary', 类名 }: ButtonProps) {
  返回 (
    <button
      className={cn(
        'rounded-md px-4 py-2',
        变体 === 'primary' && 'bg-primary text-primary-foreground',
        变体 === 'secondary' && 'bg-secondary text-secondary-foreground',
        类名,
      )}
    >
      点击我
    </button>
  )
}

Shadcn/ui 组件:

  • @/components/ui/{component-name} 导入
  • 组件已经样式设计和可访问性
  • 通过 className 属性或 CSS 变量自定义

🛣️ 路由

Next.js App Router - 文件基础:

  • 目录:app/{route-name}/page.tsx
  • 嵌套路由:app/{parent}/{child}/page.tsx
  • 动态路由:app/posts/[id]/page.tsx
  • 路由组:app/(marketing)/about/page.tsx

示例路由:

// app/posts/page.tsx
import { Metadata } from 'next'
import { PostList } from '@/components/PostList'

export const metadata: Metadata = {
  标题:'帖子',
  描述:'所有帖子列表',
}

export 默认 异步 函数 PostsPage() {
  常量 帖子 = 等待 getPosts()

  返回 (
    <div className='container mx-auto py-8'>
      <h1 className='text-3xl font-bold mb-6'>帖子</h1>
      <PostList 帖子={帖子} />
    </div>
  )
}

动态路由:

// app/posts/[id]/page.tsx
接口 PostPageProps {
  params: Promise<{ id: 字符串 }>
}

export 默认 异步 函数 PostPage({ params }: PostPageProps) {
  常量 { id } = 等待 params
  常量 帖子 = 等待 getPost(id)

  如果 (!帖子) {
    未找到()
  }

  返回 <PostDetail 帖子={帖子} />
}

⏳ 加载 & 错误状态

加载状态:

  • 在路由目录中创建 loading.tsx
  • 自动包装页面在 Suspense 中
  • 用于路由级加载

错误边界:

  • 在路由目录中创建 error.tsx
  • 自动捕获路由中的错误
  • 可以重置错误状态

示例加载 UI:

// app/posts/loading.tsx
export 默认 函数 Loading() {
  返回 (
    <div className='flex items-center justify-center min-h-screen'>
      <div className='animate-spin rounded-full h-8 w-8 边框-b-2 边框-主要' />
    </div>
  )
}

示例错误 UI:

// app/posts/error.tsx
'use client'

import { useEffect } from 'react'
import { Button } from '@/components/ui/button'

export 默认 函数 错误({
  错误,
  重置,
}: {
  错误: 错误 & { digest?: 字符串 }
  重置: () => 空
}) {
  useEffect(() => {
    控制台.error(错误)
  }, [错误])

  返回 (
    <div className='flex flex-col items-center justify-center min-h-screen'>
      <h2 className='text-2xl font-bold mb-4'>出了点问题!</h2>
      <Button onClick={重置}>再试一次</Button>
    </div>
  )
}

⚡ 性能

优化模式:

  • 使用 Server Components(更小的捆绑包)
  • 使用 next/image 用于图像
  • 使用 next/font 用于字体
  • 尽可能延迟加载 Client Components
  • 在 Client Components 中使用 useMemouseCallback
  • 使用 Suspense 边界流式传输数据

图像优化:

import Image 从 'next/image'

export 函数 Avatar({ src, alt }: { src: 字符串; alt: 字符串 }) {
  返回 (
    <Image
      src={src}
      alt={alt}
      宽度={40}
      高度={40}
      className='rounded-full'
    />
  )
}

Suspense 流式传输:

import { Suspense } from 'react'
import { PostList } from '@/components/PostList'
import { Loading } from '@/components/Loading'

export 默认 函数 页面() {
  返回 (
    <div>
      <Suspense fallback={<Loading />}>
        <PostList />
      </Suspense>
    </div>
  )
}

📘 TypeScript

标准:

  • 启用严格模式
  • 没有 any 类型
  • 函数上明确返回类型
  • 类型导入:import type { Post } from '@/types/post'
  • 组件 prop 接口与 JSDoc

示例:

import type { ComponentProps } from 'react'
import { Button } from '@/components/ui/button'

/**
 * 自定义按钮组件,带加载状态
 */
接口 CustomButtonProps 扩展 ComponentProps<typeof Button> {
  isLoading?:布尔
}

export 函数 CustomButton({
  isLoading,
  子代,
  ...props
}: CustomButtonProps) {
  返回 (
    <Button 禁用={isLoading} {...props}>
      {isLoading ? '加载中...' : 子代}
    </Button>
  )
}

🔧 常见模式

表单处理:

  • 使用 Server Actions 用于表单提交
  • 使用 react-hook-formzod 进行验证(Client Components)
  • 使用 Shadcn/ui 表单组件

示例表单与服务器操作:

// app/actions/posts.ts
'use server'

import { z } from 'zod'

常量 createPostSchema = z.object({
  标题:z.string().min(1),
  内容:z.string().min(1),
})

export 异步 函数 createPost(formData: FormData) {
  常量 rawData = {
    标题:formData.get('title'),
    内容:formData.get('content'),
  }

  常量 validated = createPostSchema.parse(rawData)
  // ... 创建帖子逻辑
  重定向('/posts')
}

元数据:

import { Metadata } from 'next'

export const metadata: Metadata = {
  标题:'帖子',
  描述:'所有帖子列表',
  开放图:{
    标题:'帖子',
    描述:'所有帖子列表',
  },
}

核心原则

  1. 首先 Server Components:默认使用 Server Components,仅在需要时使用 Client Components
  2. App Router 结构:使用文件基础路由与 app/ 目录
  3. Shadcn/ui 组件:使用预构建的可访问组件
  4. Tailwind CSS:实用程序优先样式设计与 cn() 助手
  5. TypeScript 严格:没有 any,明确类型
  6. 性能:使用 Server Components,优化图像,需要时延迟加载
  7. 文件组织:功能在 app/features/ 中,共享在 components/
  8. 导入别名:使用 @/ 前缀进行清晰导入

快速参考:文件结构

app/
  布局.tsx                    # 根布局
  页面.tsx                      # 首页
  全局.css                   # 全局样式
  (routes)/
    帖子/
      页面.tsx                  # 帖子列表页面
      [id]/
        页面.tsx                # 帖子详情页面
      加载.tsx               # 加载 UI
      错误.tsx                 # 错误 UI
  特性/
    帖子/
      components/
        帖子列表.tsx            # 功能组件
      actions/
        帖子.ts                # 服务器操作
components/
  ui/                           # Shadcn/ui 组件
    按钮.tsx
    卡片.tsx
lib/
  utils.ts                      # 实用工具(cn 等)
hooks/
  use-mobile.ts                 # 自定义钩子

现代组件模板(快速复制)

Server Component:

// app/components/PostCard.tsx
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
import 类型 { 帖子 } 从 '@/types/post'

接口 PostCardProps {
  帖子:帖子
}

export 函数 PostCard({ 帖子 }: PostCardProps) {
  返回 (
    <Card>
      <CardHeader>
        <CardTitle>{帖子.title}</CardTitle>
      </CardHeader>
      <CardContent>
        <p>{帖子.content}</p>
      </CardContent>
    </Card>
  )
}

Client Component:

// app/components/PostForm.tsx
'use client'

import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { createPost } 从 '@/app/actions/posts'
import { cn } 从 '@/lib/utils'

export 函数 PostForm({ 类名 }: { 类名?:字符串 }) {
  常量 [isLoading, setIsLoading] = useState(false)

  异步 函数 handleSubmit(formData: FormData) {
    setIsLoading(true)
    等待 createPost(formData)
    setIsLoading(false)
  }

  返回 (
    <form action={handleSubmit} className={cn('space-y-4', 类名)}>
      <Input 名称='title' placeholder='帖子标题' 必需 />
      <Input 名称='content' placeholder='帖子内容' 必需 />
      <Button 类型='submit' 禁用={isLoading}>
        {isLoading ? '创建中...' : '创建帖子'}
      </Button>
    </form>
  )
}

相关技能

  • backend-dev-guidelines:前端消费的后端 API 模式

技能状态:针对 Next.js 15 与 App Router、Server Components 和 Shadcn/ui 进行优化