从零开始用Next.js构建Todo应用:全栈开发指南

从零开始用Next.js构建Todo应用:全栈开发指南

Next.js作为当前最流行的React全栈框架之一,凭借其服务端渲染(SSR)、静态生成(SSG)和API路由等特性,成为构建现代Web应用的理想选择。本文将以Todo应用为例,详细介绍如何从零开始搭建一个包含前端界面、后端API和数据库交互的全栈应用,帮助开发者快速掌握Next.js的核心开发流程。

一、项目初始化与环境配置

1.1 创建Next.js项目

使用create-next-app命令快速初始化项目:

  1. npx create-next-app@latest todo-app --typescript
  2. cd todo-app

选择TypeScript模板可获得更好的类型安全支持。初始化完成后,项目结构将包含pagesstylespublic等基础目录。

1.2 安装必要依赖

Todo应用需要处理数据存储和API交互,推荐安装以下依赖:

  1. npm install axios @prisma/client # 用于HTTP请求和数据库操作
  2. npm install -D prisma # 数据库ORM工具

1.3 配置开发环境

next.config.js中配置环境变量支持:

  1. module.exports = {
  2. env: {
  3. DATABASE_URL: process.env.DATABASE_URL,
  4. },
  5. }

创建.env.local文件存储本地开发环境变量:

  1. DATABASE_URL="file:./dev.db" # SQLite开发数据库

二、数据库设计与Prisma集成

2.1 初始化Prisma

运行以下命令生成Prisma配置:

  1. npx prisma init

修改prisma/schema.prisma文件定义Todo模型:

  1. model Todo {
  2. id Int @id @default(autoincrement())
  3. title String
  4. completed Boolean @default(false)
  5. createdAt DateTime @default(now())
  6. }

2.2 生成数据库迁移

执行迁移命令创建数据库表:

  1. npx prisma migrate dev --name init

2.3 集成Prisma到Next.js

lib/prisma.ts中创建Prisma客户端实例:

  1. import { PrismaClient } from '@prisma/client'
  2. const globalForPrisma = globalThis as unknown as {
  3. prisma: PrismaClient
  4. }
  5. export const prisma =
  6. globalForPrisma.prisma ||
  7. new PrismaClient({
  8. log: ['query', 'info', 'warn', 'error'],
  9. })
  10. if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

三、后端API开发

3.1 创建API路由

pages/api目录下创建todos.ts文件:

  1. import { prisma } from '../../lib/prisma'
  2. import type { NextApiRequest, NextApiResponse } from 'next'
  3. export default async function handler(
  4. req: NextApiRequest,
  5. res: NextApiResponse
  6. ) {
  7. switch (req.method) {
  8. case 'GET':
  9. const todos = await prisma.todo.findMany()
  10. return res.status(200).json(todos)
  11. case 'POST':
  12. const { title } = req.body
  13. const todo = await prisma.todo.create({ data: { title } })
  14. return res.status(201).json(todo)
  15. case 'PUT':
  16. const { id, completed } = req.body
  17. const updatedTodo = await prisma.todo.update({
  18. where: { id },
  19. data: { completed },
  20. })
  21. return res.status(200).json(updatedTodo)
  22. default:
  23. return res.status(405).end()
  24. }
  25. }

3.2 API路由最佳实践

  • 验证输入:使用Zod或class-validator进行请求体验证
  • 错误处理:统一处理数据库错误并返回标准错误响应
  • 速率限制:集成中间件防止API滥用

四、前端界面开发

4.1 创建Todo列表组件

components/TodoList.tsx中实现核心功能:

  1. import { useState } from 'react'
  2. import axios from 'axios'
  3. interface Todo {
  4. id: number
  5. title: string
  6. completed: boolean
  7. }
  8. export default function TodoList() {
  9. const [todos, setTodos] = useState<Todo[]>([])
  10. const [newTodo, setNewTodo] = useState('')
  11. const fetchTodos = async () => {
  12. const response = await axios.get('/api/todos')
  13. setTodos(response.data)
  14. }
  15. const addTodo = async () => {
  16. if (!newTodo.trim()) return
  17. const response = await axios.post('/api/todos', { title: newTodo })
  18. setNewTodo('')
  19. fetchTodos()
  20. }
  21. const toggleTodo = async (id: number, completed: boolean) => {
  22. await axios.put('/api/todos', { id, completed })
  23. fetchTodos()
  24. }
  25. return (
  26. <div>
  27. <input
  28. value={newTodo}
  29. onChange={(e) => setNewTodo(e.target.value)}
  30. onKeyPress={(e) => e.key === 'Enter' && addTodo()}
  31. />
  32. <button onClick={addTodo}>Add</button>
  33. <ul>
  34. {todos.map((todo) => (
  35. <li key={todo.id}>
  36. <input
  37. type="checkbox"
  38. checked={todo.completed}
  39. onChange={(e) => toggleTodo(todo.id, e.target.checked)}
  40. />
  41. <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
  42. {todo.title}
  43. </span>
  44. </li>
  45. ))}
  46. </ul>
  47. </div>
  48. )
  49. }

4.2 页面级组件实现

pages/index.tsx中集成Todo列表:

  1. import TodoList from '../components/TodoList'
  2. import Head from 'next/head'
  3. export default function Home() {
  4. return (
  5. <div>
  6. <Head>
  7. <title>Next.js Todo App</title>
  8. </Head>
  9. <h1>My Todo List</h1>
  10. <TodoList />
  11. </div>
  12. )
  13. }

五、部署与优化

5.1 生产环境部署

推荐使用行业常见技术方案进行部署,配置步骤如下:

  1. 安装Vercel CLI:npm install -g vercel
  2. 运行vercel命令并按照提示配置项目
  3. 设置环境变量DATABASE_URL指向生产数据库

5.2 性能优化策略

  • 静态生成:对不常变化的页面使用getStaticProps
  • 数据缓存:使用SWR或React Query缓存API响应
  • 代码分割:Next.js自动实现路由级代码分割
  • 图片优化:使用next/image组件优化图片加载

六、进阶功能扩展

6.1 用户认证集成

可集成OAuth或JWT实现多用户支持:

  1. // pages/api/auth/[...nextauth].ts
  2. import NextAuth from 'next-auth'
  3. import Providers from 'next-auth/providers'
  4. export default NextAuth({
  5. providers: [
  6. Providers.GitHub({
  7. clientId: process.env.GITHUB_ID,
  8. clientSecret: process.env.GITHUB_SECRET,
  9. }),
  10. ],
  11. database: process.env.DATABASE_URL,
  12. })

6.2 实时更新功能

使用WebSocket或Server-Sent Events实现实时Todo更新:

  1. // lib/websocket.ts
  2. export const initWebSocket = () => {
  3. const ws = new WebSocket('ws://localhost:3000/api/ws')
  4. ws.onmessage = (event) => {
  5. // 处理实时更新
  6. }
  7. return ws
  8. }

七、常见问题解决方案

7.1 CORS问题处理

next.config.js中配置CORS:

  1. module.exports = {
  2. async headers() {
  3. return [
  4. {
  5. source: '/api/:path*',
  6. headers: [
  7. { key: 'Access-Control-Allow-Origin', value: '*' },
  8. { key: 'Access-Control-Allow-Methods', value: 'GET,POST,PUT' },
  9. ],
  10. },
  11. ]
  12. },
  13. }

7.2 数据库连接池配置

在Prisma配置中优化连接池:

  1. datasource db {
  2. provider = "sqlite"
  3. url = env("DATABASE_URL")
  4. // 生产环境建议使用PostgreSQL并配置连接池
  5. }

八、总结与学习资源

通过本文,开发者已掌握:

  1. Next.js项目初始化与基础配置
  2. Prisma ORM的集成与数据库操作
  3. RESTful API路由的开发实践
  4. 前后端数据交互的最佳模式
  5. 生产环境部署与性能优化技巧

推荐进一步学习资源:

  • Next.js官方文档
  • Prisma数据建模指南
  • 现代Web应用架构设计课程

完整代码示例已上传至GitHub仓库,开发者可克隆学习并扩展功能。通过这个实践项目,新手开发者能够快速建立全栈开发的知识体系,为构建更复杂的应用打下坚实基础。