学习目标
- 1理解项目结构设计的原则
- 2掌握数据类型定义的最佳实践
- 3学会组件拆分和组合
- 4实现完整的CRUD功能
欢迎来到实战章!
恭喜你走到这里!在前面的章节中,你已经掌握了:
- TypeScript基础类型和接口
- 函数和泛型
- 类和面向对象
- 异步编程
- 工具类型
- Next.js页面和API路由
- React组件
现在是时候把它们组合起来了。
这就像学做菜——你已经学会了切菜、炒菜、调味,现在要做一桌完整的饭菜。
类比理解:
| 前面学的 | 本章要做的 | |---------|----------| | 类型定义 | 设计数据结构 | | API路由 | 处理数据增删改查 | | React组件 | 构建用户界面 | | 组件组合 | 拼装完整页面 |
本章目标: 从零搭建一个完整的博客系统,包括:
- 文章列表展示
- 文章详情页面
- 创建新文章
- 编辑文章
- 删除文章
- 标签分类
- 搜索功能
第一步:定义数据类型
在开始编写代码之前,我们首先需要定义数据类型。这是TypeScript开发的重要步骤。
为什么先定义类型?
这就像盖房子之前先画图纸:
- 图纸(类型)定义了房子的结构
- 工人(代码)按照图纸施工
- 验收(TypeScript)检查是否符合图纸
先定义类型的好处:
- 明确数据结构,避免返工
- 提供代码提示,写代码更快
- 防止类型错误,代码更安全
- 作为项目文档,新人容易上手
博客系统需要的类型:
- Post(文章)
- User(用户)
- Comment(评论)
让我们开始定义这些类型。
定义文章类型
类型设计解析
让我们看看这些类型设计的思路:
PostStatus 类型
export type PostStatus = "draft" | "published" | "archived";
使用联合类型,限制文章只能是这三种状态之一。比用字符串更安全——写错字TypeScript会报错。
Post 接口 定义了文章的完整数据结构。每个字段都有明确的类型。
CreatePostInput 接口 创建文章时的输入类型。注意:
- 不包含
id(系统自动生成) - 不包含
createdAt(系统自动设置) - 不包含
viewCount(初始为0)
UpdatePostInput 接口 更新文章时的输入类型。所有字段都是可选的——你可能只想改标题,不想改内容。
设计原则:
- 读取数据用完整类型(Post)
- 创建数据用精简类型(CreatePostInput)
- 更新数据用可选类型(UpdatePostInput)
定义用户和评论类型
第二步:创建数据存储
为了简化项目,我们使用本地JSON文件存储数据。在实际项目中,你会使用数据库。
类比理解:
| 存储方式 | 类比 | 适用场景 | |---------|------|---------| | JSON文件 | 手写笔记本 | 学习、原型开发 | | SQLite | 个人日记本 | 小型项目 | | PostgreSQL | 企业档案柜 | 生产环境 |
为什么先用JSON文件?
- 不需要安装数据库
- 数据持久化(重启不会丢失)
- 易于理解和调试
- 可以轻松迁移到数据库
数据存储结构:
data/
├── posts.json # 存储所有文章
├── users.json # 存储所有用户
└── comments.json # 存储所有评论
让我们创建数据操作的工具函数。
数据操作函数
第三步:创建API路由
有了数据操作函数,现在让我们创建API路由,让前端能够访问这些数据。
RESTful API设计:
| 方法 | 路径 | 功能 | |------|------|------| | GET | /api/posts | 获取所有文章 | | GET | /api/posts/:id | 获取单篇文章 | | POST | /api/posts | 创建文章 | | PUT | /api/posts/:id | 更新文章 | | DELETE | /api/posts/:id | 删除文章 |
类比理解:
RESTful API就像图书馆的借阅系统:
- GET = 查看(不修改)
- POST = 新增一本书
- PUT = 修改书的信息
- DELETE = 删除一本书
文章API路由
第四步:创建前端页面
现在让我们创建前端页面来展示和管理文章。
页面结构:
| 页面 | 路径 | 功能 | |------|------|------| | 首页 | / | 文章列表 | | 文章详情 | /posts/[id] | 查看文章 | | 创建文章 | /posts/create | 新建文章 | | 编辑文章 | /posts/[id]/edit | 修改文章 |
组件设计:
| 组件 | 功能 | |------|------| | PostCard | 文章卡片(标题、摘要、标签) | | PostList | 文章列表(包含多个PostCard) | | PostForm | 文章表单(创建/编辑) | | SearchBar | 搜索栏 |
让我们从文章卡片组件开始。
文章卡片组件
第五步:实现搜索功能
搜索功能是博客系统的重要组成部分。让我们实现一个简单的搜索功能。
搜索实现思路:
- 创建搜索API端点
- 在前端添加搜索框
- 根据关键词过滤文章
- 显示搜索结果
搜索逻辑:
- 搜索文章标题
- 搜索文章内容
- 搜索标签
- 支持模糊匹配(不区分大小写)
搜索API和组件
常见错误
常见错误
错误1:类型定义和实际数据不匹配
// 定义了createdAt是Date,但JSON解析后是string
interface Post {
createdAt: Date; // 期望Date
}
// JSON.parse后是string!
const post = JSON.parse(data); // post.createdAt 是 "2024-01-01"
// 解决:转换日期
const post = {
...JSON.parse(data),
createdAt: new Date(parsed.createdAt),
};
错误2:忘记处理异步错误
// 错误!没有try-catch
export async function GET() {
const posts = await getAllPosts(); // 如果失败会崩溃
return NextResponse.json(posts);
}
// 正确
export async function GET() {
try {
const posts = await getAllPosts();
return NextResponse.json(posts);
} catch (error) {
return NextResponse.json({ error: "获取失败" }, { status: 500 });
}
}
错误3:组件Props类型不一致
// 传了Date对象,但组件期望string
<PostCard post={{ ...post, createdAt: new Date() }} />
// 组件内用new Date(post.createdAt)处理
// 如果传的是Date,new Date(Date)还是Date,不会报错
// 但如果传的是string,需要确保格式正确
动手实践
现在轮到你了!基于上面的代码,完成以下练习:
练习1:完善文章详情页面 创建 /app/posts/[id]/page.tsx:
- 从API获取文章数据
- 显示文章标题、内容、作者、日期
- 添加"编辑"和"删除"按钮
练习2:实现文章编辑功能 创建 /app/posts/[id]/edit/page.tsx:
- 加载现有文章数据到表单
- 提交时调用PUT /api/posts/:id
- 成功后跳转到文章详情页
练习3:添加文章删除功能 在文章详情页面添加删除功能:
- 点击删除按钮弹出确认框
- 确认后调用DELETE /api/posts/:id
- 成功后跳转到首页
练习4:实现文章列表分页
- 修改API支持分页参数(page, limit)
- 在前端添加分页组件
- 显示"上一页"和"下一页"按钮
提示:
- 使用useRouter进行页面跳转
- 使用window.confirm进行确认
- 使用try-catch处理API错误
项目总结与知识回顾
恭喜你完成了整个博客项目!让我们回顾一下你学到的所有知识:
TypeScript核心知识:
| 章节 | 知识点 | 在项目中的应用 | |------|--------|--------------| | 基础类型 | string, number, boolean, 数组 | 定义文章标题、浏览次数、标签数组 | | 接口 | interface | 定义Post、User、Comment结构 | | 函数 | 参数类型、返回值类型 | API处理函数、工具函数 | | 类 | class、继承 | 可选:面向对象的数据模型 | | 泛型 | <T> | 泛型列表组件、API响应类型 | | 工具类型 | Partial, Pick, Omit | UpdatePostInput用Partial实现 | | 异步编程 | async/await | API数据获取、文件读写 |
Next.js知识:
| 知识点 | 在项目中的应用 | |--------|--------------| | 文件系统路由 | /, /posts/[id], /posts/create | | 服务器组件 | 文章列表页面、详情页面 | | 客户端组件 | 搜索栏、表单 | | 布局 | 共享导航栏和页脚 | | API路由 | /api/posts, /api/posts/[id] |
React知识:
| 知识点 | 在项目中的应用 | |--------|--------------| | 函数组件 | PostCard, SearchBar, PostForm | | Props类型 | 组件的输入定义 | | useState | 搜索关键词、表单状态 | | 事件处理 | 表单提交、按钮点击 |
项目架构:
my-blog/
├── app/ # 页面和API
│ ├── layout.tsx # 根布局
│ ├── page.tsx # 首页
│ ├── posts/
│ │ ├── [id]/
│ │ │ ├── page.tsx # 文章详情
│ │ │ └── edit/
│ │ │ └── page.tsx # 编辑文章
│ │ └── create/
│ │ └── page.tsx # 创建文章
│ └── api/
│ └── posts/
│ ├── route.ts # 文章API
│ ├── [id]/
│ │ └── route.ts
│ └── search/
│ └── route.ts
├── components/ # 可复用组件
│ ├── PostCard.tsx
│ ├── PostForm.tsx
│ └── SearchBar.tsx
├── lib/ # 工具函数
│ └── posts.ts
├── types/ # 类型定义
│ ├── post.ts
│ ├── user.ts
│ └── comment.ts
└── data/ # 数据文件
├── posts.json
├── users.json
└── comments.json
后续学习建议
你已经完成了TypeScript + Next.js的基础学习!接下来可以继续深入:
短期目标(1-2周):
- 部署项目 - 使用Vercel一键部署
- 添加数据库 - 使用Prisma + SQLite/PostgreSQL
- 用户认证 - 使用NextAuth.js实现登录
- 样式优化 - 深入学习Tailwind CSS
中期目标(1-2个月):
- 测试 - 学习Jest和React Testing Library
- 状态管理 - 学习Zustand或Jotai
- 性能优化 - 学习Next.js的缓存策略
- CI/CD - 自动化测试和部署
长期目标(3-6个月):
- 全栈框架 - 深入学习Next.js高级特性
- AI集成 - 使用Vercel AI SDK添加AI功能
- 微服务 - 学习系统架构设计
- 开源贡献 - 参与开源项目
学习资源推荐:
- 官方文档:nextjs.org/docs, typescriptlang.org
- 视频教程:YouTube搜索"Next.js tutorial"
- 实战项目:GitHub搜索"next.js projects"
- 社区:Reddit r/nextjs, Discord
最后的话:
TypeScript和Next.js是现代Web开发的核心技能。你已经迈出了重要的一步——从零开始搭建了一个完整的博客系统。
记住:最好的学习方式是动手实践。不要只是看教程,要自己写代码、踩坑、解决问题。
祝你学习愉快,期待看到你的作品!
交互式练习
定义文章类型
根据需求定义一个完整的文章接口
章节测验
在TypeScript中,定义接口使用什么关键字?