Next.js 全栈开发入门指南(2026 最新版)
# 文档说明
本文档面向Web开发初学者,全程无晦涩术语、代码可直接复制运行。你将系统掌握Next.js的核心定位、解决的行业痛点、与传统开发模式的核心差异,从0搭建项目、吃透三大核心渲染模式、完成「调用第三方API+数据库增删改查」的全栈实战,最终掌握企业级最佳实践与适用场景,看完即可独立开发上线完整项目。
# 一、什么是Next.js?
Next.js 是由Vercel公司开发、Meta官方推荐的React全栈Web开发框架。
它把前端页面渲染、后端服务逻辑、路由系统、数据获取、性能优化、部署上线等全流程能力整合到一套代码中,彻底打破了「前端只管页面、后端只管接口」的传统壁垒,让你只用React语法,就能开发出高性能、SEO友好、安全可靠的全栈Web应用。
简单说:一套React代码,搞定前端+后端,不用再分开写前端项目和后端接口项目。
# 二、传统Web开发的核心痛点(Next.js的解决之道)
在Next.js普及之前,Web开发主要分为两种模式,均存在无法规避的核心痛点,这也是Next.js诞生的核心原因。
# 2.1 两种传统开发模式的致命痛点
| 传统开发模式 | 核心特点 | 无法解决的痛点 |
|---|---|---|
| 传统服务端渲染(PHP/JSP/Java模板) | 页面在服务端生成HTML返回给浏览器,前后端代码高度耦合 | 1. 前后端强绑定,前端改页面需要后端配合,开发效率极低; 2. 页面跳转全量刷新,用户体验差; 3. 无法使用现代React/Vue前端生态,交互能力弱 |
| 前后端分离(React SPA + 独立后端API) | 前端单独写单页应用,通过fetch/axios调用后端接口,前后端完全分离 | 1. 首屏白屏严重:需要先下载完整JS包,再渲染页面、请求数据,低端机/弱网体验极差; 2. SEO天生残疾:页面内容由JS动态渲染,搜索引擎爬虫无法抓取有效内容; 3. 开发成本高:一个简单的用户新增功能,需要前端写页面、后端写接口、双方联调、处理跨域/错误/loading,代码量翻倍; 4. 安全风险高:前端无法直接操作数据库,所有数据操作依赖接口,接口多了极易出现权限遗漏、数据越权问题; 5. 运维复杂:前端部署CDN、后端部署服务器,需要配置跨域、HTTPS、缓存,出问题需要两端排查 |
# 2.2 Next.js 如何一次性解决所有痛点?
针对以上痛点,Next.js给出了一站式解决方案,核心能力如下:
多渲染模式自由切换:同时支持SSR(服务端渲染)、SSG(静态生成)、ISR(增量静态更新)、CSR(客户端渲染),既解决了SPA的白屏&SEO问题,又保留了现代前端的丝滑交互体验;
Server Actions 全栈能力:无需单独写API接口,直接在React组件中编写服务端函数,即可安全操作数据库、调用私密接口,彻底告别「接口联调地狱」;
文件式路由系统:文件夹结构=页面路由,无需手动配置路由规则,零配置实现嵌套路由、动态路由、权限路由;
内置全自动性能优化:图片自动压缩/懒加载、代码自动分割、路由预加载、字体优化,新手也能写出高性能页面;
服务端代码天然隔离:服务端逻辑永远不会发送到浏览器,数据库密码、API密钥等敏感信息100%安全;
一体化部署:一套代码一键部署,无需分开维护前后端服务,彻底解决跨域、版本不同步问题。
# 三、Next.js 与传统Web开发的核心优势对比
| 对比维度 | 传统前后端分离 | 传统PHP/JSP服务端渲染 | Next.js 全栈框架 |
|---|---|---|---|
| 首屏加载速度 | 慢,白屏时间长 | 较快,跳转全量刷新 | 极快,服务端返回完整HTML,跳转无刷新 |
| SEO友好度 | 差,需额外做预渲染改造 | 良好,内容直接返回 | 优秀,天然支持所有搜索引擎抓取 |
| 开发效率 | 低,需写接口、联调、处理跨域 | 低,前后端耦合,改造成本高 | 极高,一套代码搞定前后端,零接口联调 |
| 数据安全性 | 依赖接口权限校验,易出现漏洞 | 较好,逻辑在服务端 | 优秀,服务端代码物理隔离,内置CSRF防护 |
| 交互体验 | 优秀,无刷新跳转 | 差,页面全量刷新 | 优秀,内置路由预加载,原生级交互体验 |
| 性能优化门槛 | 高,需手动配置代码分割、图片优化等 | 低,优化能力有限 | 极低,框架内置全自动优化,开箱即用 |
| 部署运维成本 | 高,前后端分开部署、配置 | 中,需配置服务端环境 | 极低,一键部署,一体化运维 |
| 无JS兼容性 | 完全不可用,页面白屏 | 可用,交互能力弱 | 可用,内容正常展示,表单可正常提交 |
# 四、快速入门:5分钟从0搭建第一个Next.js项目
# 4.1 环境准备
仅需安装 Node.js 18.17及以上版本(官网下载LTS版本,一路默认安装即可),安装完成后在终端执行以下命令,能输出版本号即安装成功:
node -v
# 4.2 创建项目
打开终端,进入你想存放项目的文件夹,执行以下创建命令:
npx create-next-app@latest my-first-next-app
执行后会出现配置选项,新手一路按回车选择默认配置即可,默认配置包含:TypeScript、ESLint、Tailwind CSS、App Router(最新路由系统)。
# 4.3 启动项目
创建完成后,依次执行以下命令进入项目、启动开发服务器:
# 进入项目文件夹
cd my-first-next-app
# 启动开发服务器
npm run dev
2
3
4
启动成功后,打开浏览器访问 http://localhost:3000,看到Next.js欢迎页面,即项目搭建成功!
# 4.4 新手必懂的核心项目结构
my-first-next-app/
├── app/ 【核心】路由文件夹,文件结构=页面路由
│ ├── page.tsx 首页,对应访问路径 /
│ ├── layout.tsx 全局布局文件,所有页面共用
│ └── about/ 新建about文件夹,对应路由 /about
│ └── page.tsx 关于页面,对应访问路径 /about
├── public/ 静态资源文件夹,存放图片、图标等
├── lib/ 工具类文件夹,存放数据库连接、工具函数等(需手动创建)
└── package.json 项目依赖配置文件
2
3
4
5
6
7
8
9
核心规则:app 文件夹下,每一个 page.tsx 文件,都会自动生成一个对应路径的页面,无需手动配置路由,路由分为三类:
基础规则: app/ 文件夹内部的层级结构 = 网址 URL只有创建了 page.tsx,这个路径才能被访问
固定路由: 文件夹名不带 [] = 写死的固定网址
app/about/page.tsx → 访问 /about app/blog/page.tsx → 访问 /blog1
2动态路由: 文件夹名带 [] = 可变参数的动态网址
app/blog/[slug]/page.tsx → 访问 /blog/1、/blog/abc、/blog/任意值1
代码目录和路由关系:
你的项目目录 → 浏览器访问地址
└── app/
├── page.tsx → localhost:3000/ (首页)
├── about/
│ └── page.tsx → localhost:3000/about (固定路由)
└── blog/
├── page.tsx → localhost:3000/blog (固定路由)
└── [id]/
└── page.tsx → localhost:3000/blog/123 (动态路由)
2
3
4
5
6
7
8
9
# 五、Next.js 四大核心渲染模式详解(含代码示例)
渲染模式是Next.js的核心灵魂,它决定了页面何时生成、数据何时获取,直接影响页面性能、SEO和服务器压力。Next.js App Router 原生支持四大核心渲染模式(SSR、SSG、ISR、CSR),无需复杂配置,几行代码即可切换,所有示例均基于公开的文章API,可直接复制运行。
# 5.1 SSG(Static Site Generation 静态站点生成)
# 核心定义
SSG是Next.js默认的渲染模式,也是性能最优的模式。它会在项目构建阶段(执行npm run build时),提前执行数据获取、渲染生成完整的静态HTML文件,部署后所有用户访问都直接返回这个预先生成的静态文件,无需服务端重复执行代码、查询接口/数据库。
特点:
build时一次性生成 HTML- 部署后内容不会变
- 最快、最利于 SEO
# 适用场景
不常更新的静态内容页面:企业官网、个人博客、帮助文档、营销落地页、静态新闻列表。
# 核心优势
访问速度极快:静态文件可直接托管在CDN,用户打开秒开;
零服务器运行压力:无需每次请求执行服务端逻辑,高并发无压力;
极致SEO友好:完整HTML内容直接被搜索引擎抓取;
稳定性极强:无服务端代码执行,不会出现接口超时等运行时错误。
# 完整代码示例
新建文件 app/ssg-posts/page.tsx,写入以下代码:
// app/ssg-posts/page.tsx 对应访问路径 /ssg-posts
// 无任何强制动态配置,Next.js默认使用SSG静态生成
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
// 构建时执行数据获取,生成静态页面
async function getStaticPosts(): Promise<Post[]> {
// fetch默认开启强制缓存,构建时拉取数据后永久缓存
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
if (!res.ok) throw new Error('数据获取失败');
return res.json();
}
// 页面组件
export default async function SSGPostsPage() {
// 构建时获取数据,前端访问直接返回预渲染的HTML
const posts = await getStaticPosts();
return (
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-8">SSG静态生成文章列表</h1>
<p className="mb-6 text-gray-500">本页面在构建时预渲染,所有用户访问均返回静态HTML</p>
<div className="space-y-4">
{posts.map((post) => (
<div key={post.id} className="border rounded-lg p-4 shadow-sm">
<h2 className="text-xl font-semibold mb-2">{post.title}</h2>
<p className="text-gray-600">{post.body}</p>
</div>
))}
</div>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 验证方式
执行 npm run build 打包项目,在打包日志中,该页面会标记为 ○ (Static),即代表SSG静态生成成功。
# 5.2 SSR(Server-Side Rendering 服务端渲染)
# 核心定义
SSR即动态服务端渲染,用户每次发起页面请求时,服务端都会实时执行数据获取、渲染完整的HTML页面,再返回给浏览器。每次请求都会重新执行代码、拉取最新数据。
特点:
- 每次访问都重新生成 HTML
- 数据永远最新
- 性能比 SSG/ISR 差
# 适用场景
强实时性、强个性化的动态页面:实时数据仪表盘、个人中心、库存实时变动的商品详情页、需要实时权限校验的页面。
# 核心优势
数据永远最新:每次请求都拉取实时数据,无缓存延迟;
SEO友好:完整HTML内容直接返回,搜索引擎可正常抓取;
个性化能力强:可根据用户请求信息(Cookie、登录态)返回个性化内容;
首屏性能优秀:无需等待客户端JS下载执行,直接返回渲染好的页面。
# 完整代码示例
新建文件 app/ssr-posts/page.tsx,写入以下代码:
// app/ssr-posts/page.tsx 对应访问路径 /ssr-posts
// 强制动态渲染,每次请求都重新执行服务端逻辑
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
// 每次用户请求页面时,都会实时执行该函数拉取最新数据
async function getDynamicPosts(): Promise<Post[]> {
// cache: 'no-store' 强制不缓存,每次请求都重新调用接口
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10', {
cache: 'no-store'
});
if (!res.ok) throw new Error('数据获取失败');
return res.json();
}
// 页面组件
export default async function SSRPostsPage() {
// 每次请求都实时获取最新数据
const posts = await getDynamicPosts();
// 可获取当前请求时间,验证每次刷新都会更新
const requestTime = new Date().toLocaleString();
return (
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-4">SSR服务端渲染文章列表</h1>
<p className="mb-6 text-gray-500">本页面每次刷新都会重新渲染,当前请求时间:{requestTime}</p>
<div className="space-y-4">
{posts.map((post) => (
<div key={post.id} className="border rounded-lg p-4 shadow-sm">
<h2 className="text-xl font-semibold mb-2">{post.title}</h2>
<p className="text-gray-600">{post.body}</p>
</div>
))}
</div>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 验证方式
执行
npm run build打包项目,在打包日志中,该页面会标记为λ (Dynamic),即代表SSR动态渲染;启动生产服务
npm run start,访问页面后多次刷新,可看到页面上的请求时间每次都会更新,验证每次请求都重新渲染。
# 5.3 ISR(Incremental Static Regeneration 增量静态再生成)
# 核心定义
ISR是Next.js的王牌功能,完美结合了SSG的极致性能和SSR的实时性优势。它在构建时预生成静态页面,部署后可按照你设定的时间间隔,在后台自动重新拉取数据、渲染更新静态页面,无需重新构建、重新部署项目。
简单说:静态页面的访问速度,同时支持数据自动定期更新。
特点:
- 先 SSG,定时自动刷新
- 访问快 + 数据能更新
- 用
revalidate控制刷新时间
# 适用场景
需要定期更新、但无需实时的页面:新闻资讯列表、电商商品列表、博客文章、论坛帖子列表,访问量高但更新频率不高的内容。
# 核心优势
兼顾速度与新鲜度:既有SSG的静态访问速度,又能定期更新数据;
极低的服务器压力:无论多少用户访问,仅在设定的间隔内执行一次数据更新;
无需重新部署:内容更新无需重新打包构建,后台自动完成;
容错能力强:即使更新时接口出错,仍会返回之前的静态页面,不会影响用户访问。
# 完整代码示例
新建文件 app/isr-posts/page.tsx,写入以下代码:
// app/isr-posts/page.tsx 对应访问路径 /isr-posts
// 增量静态再生成,设置60秒重新验证更新一次数据
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
// 设定页面缓存有效期,单位:秒
// 60秒内的所有访问,都返回缓存的静态页面;60秒后首次访问,后台自动更新页面
export const revalidate = 60;
// 构建时预执行,后续每60秒自动重新执行更新数据
async function getISRPosts(): Promise<Post[]> {
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
if (!res.ok) throw new Error('数据获取失败');
return res.json();
}
// 页面组件
export default async function ISRPostsPage() {
const posts = await getISRPosts();
// 页面生成时间,验证缓存更新
const generateTime = new Date().toLocaleString();
return (
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-4">ISR增量静态更新文章列表</h1>
<p className="mb-6 text-gray-500">本页面每60秒自动更新一次,当前页面生成时间:{generateTime}</p>
<div className="space-y-4">
{posts.map((post) => (
<div key={post.id} className="border rounded-lg p-4 shadow-sm">
<h2 className="text-xl font-semibold mb-2">{post.title}</h2>
<p className="text-gray-600">{post.body}</p>
</div>
))}
</div>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 验证方式
执行
npm run build打包项目,在打包日志中,该页面会标记为● (ISR),即代表增量静态更新配置成功;启动生产服务
npm run start,首次访问页面记录生成时间,后续60秒内刷新,时间不会变化(返回缓存静态页);60秒后再次刷新,页面会更新为最新的生成时间,验证后台自动更新生效。
# 5.4 CSR(Client-Side Rendering 客户端渲染)
# 核心定义
CSR即客户端渲染,是传统React SPA的核心渲染模式。服务端仅返回一个空的HTML模板(包含JS引用),页面渲染、数据获取等逻辑全部在浏览器端(客户端)执行,通过JS动态生成页面内容并插入到HTML中,完成页面展示。
简单说:服务端只给“空壳”,浏览器自己加载JS、获取数据、渲染页面,也是Next.js中需手动开启的渲染模式(标记'use client')。
特点:
- 纯前端渲染,首屏无内容
- 数据在浏览器里请求
- 不利于 SEO
- 无缓存
# 适用场景
强交互、无SEO需求、数据频繁变动的页面:后台管理系统、用户个人中心(非公开内容)、在线编辑器、社交应用动态 feed、游戏类Web应用。
# 核心优势
交互体验极致流畅:页面渲染和数据更新都在客户端完成,无需请求服务端,跳转、操作无延迟;
减轻服务端压力:服务端仅返回静态模板,无需执行渲染和数据查询逻辑,高并发场景下优势明显;
动态交互能力强:支持复杂的用户交互(如拖拽、实时输入反馈、弹窗联动),适配需要大量JS交互的场景;
开发灵活:完全沿用传统React开发模式,熟悉React的开发者可快速上手,无需额外学习服务端渲染逻辑。
# 完整代码示例
新建文件 app/csr-posts/page.tsx,写入以下代码(明确标记客户端渲染,适配复杂交互场景):
// app/csr-posts/page.tsx 对应访问路径 /csr-posts
'use client'; // 标记为客户端组件,强制开启CSR客户端渲染
import { useState, useEffect } from 'react';
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
export default function CSRPostsPage() {
// 客户端维护状态,用于交互和数据展示
const [posts, setPosts] = useState<Post[]>([]);
const [loading, setLoading] = useState(true);
const [searchKey, setSearchKey] = useState(''); // 客户端交互状态(搜索)
// 客户端数据获取,在浏览器端执行
const fetchPosts = async () => {
setLoading(true);
try {
// 客户端请求API,可根据交互状态(如搜索关键词)动态调整请求参数
const res = await fetch(`https://jsonplaceholder.typicode.com/posts?q=${searchKey}`);
if (!res.ok) throw new Error('数据获取失败');
const data = await res.json();
setPosts(data.slice(0, 10)); // 截取前10条数据
} catch (err) {
console.error('客户端请求失败', err);
} finally {
setLoading(false);
}
};
// 组件挂载时获取初始数据,搜索关键词变化时重新获取数据
useEffect(() => {
fetchPosts();
}, [searchKey]);
return (
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-8">CSR客户端渲染文章列表</h1>
<p className="mb-6 text-gray-500">本页面在浏览器端渲染,数据获取和页面更新均在客户端完成</p>
// 客户端交互组件(搜索框),体现CSR交互优势
<div className="mb-6">
<input
type="text"
placeholder="输入关键词搜索文章..."
value={searchKey}
onChange={(e) => setSearchKey(e.target.value)}
className="w-full px-4 py-2 border rounded-lg shadow-sm"
/>
</div>
{loading ? (
<div className="p-6 text-center">加载中...</div>
) : (
<div className="space-y-4">
{posts.map((post) => (
<div key={post.id} className="border rounded-lg p-4 shadow-sm">
<h2 className="text-xl font-semibold mb-2">{post.title}</h2>
<p className="text-gray-600">{post.body.substring(0, 100)}...</p>
</div>
))}
</div>
)}
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 验证方式
执行
npm run dev启动开发服务,访问http://localhost:3000/csr-posts,可看到页面先显示“加载中”,随后渲染内容(验证客户端渲染流程);打开浏览器“开发者工具”,切换到「Network」面板,刷新页面,可看到服务端仅返回
csr-posts相关的HTML和JS文件,数据请求(fetch)是在客户端发起的;在搜索框输入关键词(如“test”),页面会实时重新请求数据并更新,无需刷新页面,验证客户端交互与动态渲染能力。
# 六、核心实战:2个必学示例(调用第三方API + 数据库增删改查)
以下示例代码均可直接复制运行,全程基于Next.js最新的App Router和Server Components,符合企业级开发规范。
# 示例1:调用第三方API(2种常用方式,新手必懂)
我们以公开的「文章数据API」为例,地址:https://jsonplaceholder.typicode.com/posts,无需申请密钥,可直接调用。
Next.js中调用API分为服务端调用和客户端调用,优先推荐服务端调用,性能更好、SEO更友好、无跨域问题。
# 方式1:服务端组件调用(推荐,90%场景首选)
Next.js中,没有加 'use client' 标记的组件,默认都是服务端组件,代码只在服务端运行,可直接用 async/await 调用API,无需useEffect、无需处理loading状态。
新建文件 app/posts/page.tsx,写入以下代码:
// app/posts/page.tsx 对应访问路径 /posts
// 无'use client',默认是服务端组件,代码只在服务端运行
// 定义接口返回数据类型
interface Post {
id: number;
title: string;
body: string;
}
// 服务端调用第三方API
async function getPosts(): Promise<Post[]> {
// 调用第三方API,Next.js默认会缓存结果,避免重复请求
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
if (!res.ok) throw new Error('接口请求失败');
return res.json();
}
// 页面组件,直接async/await获取数据
export default async function PostsPage() {
// 直接在组件中获取数据,服务端执行,前端看不到接口调用逻辑
const posts = await getPosts();
return (
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-8">第三方API文章列表</h1>
<div className="space-y-4">
{posts.map((post) => (
<div key={post.id} className="border rounded-lg p-4 shadow-sm">
<h2 className="text-xl font-semibold mb-2">{post.title}</h2>
<p className="text-gray-600">{post.body}</p>
</div>
))}
</div>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
保存后,访问 http://localhost:3000/posts,即可看到渲染好的文章列表。
核心优势:
- 页面打开直接显示内容,无白屏、无loading等待;
- 搜索引擎直接抓取到完整内容,SEO拉满;
- 接口调用在服务端完成,前端看不到接口地址,无跨域问题;
- Next.js默认缓存接口结果,重复访问不会重复调用第三方API。
# 方式2:客户端组件调用(仅用于需要实时交互的场景)
如果你的页面需要按钮点击、实时输入等用户交互,需要使用客户端组件,加 'use client' 标记,用法和传统React一致。
新建文件 app/client-posts/page.tsx,写入以下代码:
// app/client-posts/page.tsx 对应访问路径 /client-posts
'use client'; // 标记为客户端组件,代码会发送到浏览器
import { useEffect, useState } from 'react';
interface Post {
id: number;
title: string;
body: string;
}
export default function ClientPostsPage() {
const [posts, setPosts] = useState<Post[]>([]);
const [loading, setLoading] = useState(true);
// 客户端调用API,和传统React用法一致
useEffect(() => {
const fetchPosts = async () => {
try {
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
const data = await res.json();
setPosts(data);
} catch (err) {
console.error('请求失败', err);
} finally {
setLoading(false);
}
};
fetchPosts();
}, []);
if (loading) return <div className="p-6">加载中...</div>;
return (
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-8">客户端调用API文章列表</h1>
<div className="space-y-4">
{posts.map((post) => (
<div key={post.id} className="border rounded-lg p-4 shadow-sm">
<h2 className="text-xl font-semibold mb-2">{post.title}</h2>
<p className="text-gray-600">{post.body}</p>
</div>
))}
</div>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 示例2:连接数据库,实现完整增删改查(CRUD)
我们使用SQLite作为数据库,无需安装任何数据库服务、无需配置账号密码,开箱即用,新手零门槛。搭配Next.js的Server Actions,无需写任何API接口,即可安全实现数据库增删改查。
# 步骤1:安装依赖
在项目终端执行以下命令,安装数据库依赖:
npm install better-sqlite3
# 类型声明,TypeScript项目需要
npm install -D @types/better-sqlite3
2
3
# 步骤2:创建数据库连接工具类
新建 lib/db.ts 文件,写入以下代码,实现数据库连接和表初始化:
// lib/db.ts
import Database from 'better-sqlite3';
// 连接数据库,自动创建db.sqlite3文件,无需手动创建
const db = new Database('db.sqlite3');
// 初始化用户表,第一次运行自动创建,后续运行不会重复创建
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
export default db;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 步骤3:创建Server Actions,实现增删改查逻辑
新建 app/users/actions.ts 文件,写入以下代码,所有函数都标记为服务端操作,代码只在服务端运行,绝对不会暴露给前端:
// app/users/actions.ts
'use server'; // 标记整个文件的函数都是Server Actions,只在服务端运行
import db from '@/lib/db';
import { revalidatePath } from 'next/cache';
// 1. 新增用户
export async function addUser(formData: FormData) {
// 从表单中获取数据
const name = formData.get('name') as string;
const email = formData.get('email') as string;
if (!name || !email) throw new Error('姓名和邮箱不能为空');
try {
// 执行数据库插入操作
db.prepare('INSERT INTO users (name, email) VALUES (?, ?)').run(name, email);
// 刷新页面数据,让用户看到最新的列表
revalidatePath('/users');
return { success: true, message: '用户添加成功' };
} catch (err) {
console.error('添加用户失败', err);
return { success: false, message: '邮箱已存在,添加失败' };
}
}
// 2. 删除用户
export async function deleteUser(id: number) {
try {
db.prepare('DELETE FROM users WHERE id = ?').run(id);
revalidatePath('/users');
return { success: true };
} catch (err) {
console.error('删除用户失败', err);
return { success: false, message: '删除失败' };
}
}
// 3. 修改用户
export async function updateUser(formData: FormData) {
const id = Number(formData.get('id'));
const name = formData.get('name') as string;
if (!id || !name) throw new Error('参数错误');
try {
db.prepare('UPDATE users SET name = ? WHERE id = ?').run(name, id);
revalidatePath('/users');
return { success: true, message: '修改成功' };
} catch (err) {
console.error('修改用户失败', err);
return { success: false, message: '修改失败' };
}
}
// 4. 查询用户列表
export function getUsers() {
return db.prepare('SELECT * FROM users ORDER BY create_time DESC').all();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 步骤4:创建页面,实现完整的用户管理功能
新建 app/users/page.tsx 文件,写入以下代码,实现表单提交、用户列表展示、修改和删除功能:
// app/users/page.tsx 对应访问路径 /users
import { addUser, deleteUser, getUsers, updateUser } from './actions';
export default function UsersPage() {
// 服务端直接查询数据库,前端看不到任何数据库逻辑
const users = getUsers();
return (
<div className="max-w-4xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-8">用户管理系统(数据库增删改查)</h1>
{/* 新增用户表单,直接绑定Server Action,无需写onSubmit、无需写fetch */}
<form action={addUser} className="flex gap-3 mb-10 p-4 border rounded-lg shadow-sm">
<input
type="text"
name="name"
placeholder="请输入姓名"
className="flex-1 px-3 py-2 border rounded"
required
/>
<input
type="email"
name="email"
placeholder="请输入邮箱"
className="flex-1 px-3 py-2 border rounded"
required
/>
<button
type="submit"
className="px-6 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
添加用户
</button>
</form>
{/* 用户列表 */}
<div className="border rounded-lg overflow-hidden">
<table className="w-full">
<thead className="bg-gray-50 border-b">
<tr>
<th className="px-4 py-3 text-left">ID</th>
<th className="px-4 py-3 text-left">姓名</th>
<th className="px-4 py-3 text-left">邮箱</th>
<th className="px-4 py-3 text-left">创建时间</th>
<th className="px-4 py-3 text-center">操作</th>
</tr>
</thead>
<tbody className="divide-y">
{users.length === 0 ? (
<tr>
<td colSpan={5} className="px-4 py-8 text-center text-gray-500">
暂无用户数据,请添加用户
</td>
</tr>
) : (
users.map((user: any) => (
<tr key={user.id}>
<td className="px-4 py-3">{user.id}</td>
<td className="px-4 py-3">{user.name}</td>
<td className="px-4 py-3">{user.email}</td>
<td className="px-4 py-3">{user.create_time}</td>
<td className="px-4 py-3 flex justify-center gap-2">
{/* 修改用户表单 */}
<form action={updateUser} className="inline">
<input type="hidden" name="id" value={user.id} />
<input
type="text"
name="name"
placeholder="新姓名"
className="w-24 px-2 py-1 border rounded"
required
/>
<button
type="submit"
className="ml-1 px-3 py-1 bg-green-500 text-white rounded hover:bg-green-600"
>
修改
</button>
</form>
{/* 删除用户表单 */}
<form action={deleteUser.bind(null, user.id)} className="inline">
<button
type="submit"
className="px-3 py-1 bg-red-500 text-white rounded hover:bg-red-600"
>
删除
</button>
</form>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# 步骤5:运行效果
保存所有代码后,访问 http://localhost:3000/users,你即可实现:
- 表单提交添加用户,直接写入数据库;
- 实时展示数据库中的用户列表;
- 一键修改用户姓名,更新数据库;
- 一键删除用户,同步数据库;
- 所有操作无刷新、无需写任何API接口、数据库逻辑100%安全,不会暴露给前端。
# 七、Next.js 企业级开发最佳实践(新手照做不踩坑)
# 7.1 渲染模式选择最佳实践
- 能静态就静态:官网、博客、文档等不常更新的页面,优先使用SSG静态生成,零服务器压力、访问速度最快;
- 动态内容加缓存:商品列表、新闻等需要定期更新的页面,使用ISR增量静态更新,设置
revalidate = 60(缓存60秒),既保证数据新鲜度,又避免频繁查询数据库; - 仅实时场景用纯SSR:只有后台实时数据、个人中心等强个性化、强实时性的页面,才使用
cache: 'no-store'强制不缓存,避免滥用导致服务器压力过大; - 客户端组件最小化:只有需要用户交互(按钮点击、输入框、状态管理)的组件,才加
'use client'标记,其余全部用服务端组件,保证性能和安全。
# 7.2 安全开发最佳实践
- 永远不要在客户端组件中写数据库连接、密钥等敏感信息,所有敏感操作必须放在Server Actions或服务端组件中;
- 环境变量规范:只有需要暴露给前端的变量,才以
NEXT_PUBLIC_开头,数据库密码、API密钥等敏感变量,绝对不能加此前缀; - Server Actions 必须做参数校验:所有用户提交的数据,必须做格式校验、权限校验,不能直接写入数据库;
- 禁止在客户端组件中内联Server Actions,客户端组件必须导入单独文件的Server Actions,避免代码泄露。
# 7.3 性能优化最佳实践
- 使用内置的Image组件:所有图片都用
next/image,自动实现压缩、懒加载、自适应,避免图片导致的性能问题; - 使用内置的Font组件:用
next/font加载字体,避免字体闪烁、加载慢的问题; - 路由预加载:用
next/link做页面跳转,Next.js会自动预加载目标页面,实现秒开跳转; - 避免大体积客户端组件:大的交互组件,使用动态导入
dynamic(() => import('./xxx')),实现按需加载,减少首屏JS体积。
# 7.4 部署最佳实践
- 优先选择Vercel部署:Next.js官方出品,一键部署、零配置、自动扩容,免费额度足够个人项目和小型企业项目使用;
- 自有服务器部署:可使用Docker打包部署,或执行
npm run build打包后,用npm run start启动生产服务; - 生产环境必须关闭开发模式,避免源码泄露和性能问题。
# 八、Next.js 适用场景 & 不适用场景
# 8.1 强烈推荐使用的场景
- 企业官网、品牌官网、营销落地页:SEO友好、首屏秒开、部署简单,完美匹配需求;
- 博客、文档站、内容门户、新闻站点:SSG+ISR组合,既保证访问速度,又能轻松更新内容;
- 电子商务平台、商城站点:SSR+ISR兼顾SEO和实时价格/库存更新,Server Actions安全处理订单逻辑;
- SaaS应用、后台管理系统、仪表盘:全栈一体化开发,一套代码搞定页面和后端逻辑,开发效率翻倍;
- AI应用、工具类网站:服务端安全调用AI API,避免密钥泄露,同时保证前端交互体验;
- 移动端优先的Web应用、PWA:内置性能优化,弱网环境体验远超传统SPA。
# 8.2 不推荐使用的场景
- 纯静态无动态内容的单页:比如只有1-2个页面的纯静态介绍页,可选择更轻量的Astro、VitePress;
- 已有成熟超大型微服务后端体系:不建议用Next.js替代后端,可作为前端渲染层,调用现有微服务接口;
- 非React技术栈项目:Next.js基于React,Vue/Angular技术栈建议选择对应框架(Nuxt、Analog)。
# 九、总结
# 核心总结
Next.js 彻底重构了现代Web开发的流程,它不是简单的React框架,而是一套完整的全栈开发解决方案:
- 它解决了传统前后端分离的SEO、首屏性能、开发效率痛点;
- 它保留了现代前端的丝滑交互体验,同时补齐了服务端能力;
- 它让前端开发者无需学习额外的后端语言,只用React语法,就能开发完整的全栈应用;
- 它的企业级生态和性能优化能力,让新手也能写出符合生产标准的Web项目。