润物细无声。这就是好的技术栈,让你不知不觉就用上了。
大家好,我是双越老师~
背景
3 月初宣布做一个 Node 全栈 AIGC 知识库项目 划水AI (类似 Notion AI 和协同编辑),受到很多同学的关注和支持。
项目背景和功能可看这里 前端转全栈: Next.js + ChatGPT 开发 AIGC 知识库(AI 写作) 欢迎围观~
想围观、学习、参与开发的同学,可以私信我~
项目计划 4 月正式开始,现在我正在做一些调研和准备工作。项目的核心技术选型是 React 和 Next.js 。某些模块也会使用 Nest.js ,扩充项目的技术广度。
本文是 Next.js 的介绍和入门,主要给已经参与了 划水AI 项目的同学,让他们快速学习 Next.js 。源代码在文章末尾。
为何选择 React 和 Next.js
因为简单。
上周我写过一篇文章 【长文】只会 Vue 不会 React ?22 点证明 React 比 Vue3 更简单 ,从多个方面介绍了 React 其实非常简单,会 JS 就会 React 。
其实 Next.js 也一样,它没有太多新概念,只要你会 React 和 HTTP 协议,你就能轻松入门 Next.js ,不需要你熟悉很多 Node.js 的知识。不信可以阅读完本文,如果没有理解上的障碍,那就是已经入门了。
PS:可以对比一下隔壁的 Nest.js ,上来就是一堆概念(Module Service Controller...),代码里还有各种 @
装饰器语法。很多前端同学都没见过。
创建项目
参照 Next.js 文档 一键安装。
PS:推荐使用 tailwindcss ,这也是 Next.js 推荐的 CSS 方案,很多 example 都会用它
运行 npm run dev
然后浏览器访问 http://localhost:3000
即可。
修改首页内容
源码中,打开 src/app/page.tsx
,这是首页的代码,都是 React 组件,可以随意修改它。
想要修改网页 head 内容(即 metadata),可在 src/app/layout.tsx
中修改。
ts
// 改为你自己的标题、描述,或者扩展其他的 metadata
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
创建一个页面
新建一个目录 src/app/blogs
,然后在其中新建一个文件 page.tsx
,代码如下
tsx
// src/app/blogs/page.tsx
export default function Blogs() {
return <div>
<h1 className="text-3xl">Blogs</h1>
<p>博客列表</p>
</div>
}
然后在浏览器访问 http://localhost:3000/blogs
即可看到效果。
你还可以在 blogs
下面再继续新建目录和文档 src/app/blogs/test/page.tsx
,然后浏览器访问 http://localhost:3000/blogs/test
。
对,这就是 Next.js 的路由 规则,非常简单。你想定义怎样的路由,你就创建相应的目录,然后在文件夹中新建一个 page.tsx
返回一个 React 组件即可。
这样的路由规则比 React-router 还要简单,还要容易理解。
动态页面
创建目录 src/app/blogs/[id]
,在其中创建 page.tsx
文件,可以定义动态页面。React 组件中可以使用 params.id
获取参数。
浏览器访问 http://localhost:3000/blogs/123
即可看到效果 (其中 123
是 id,自行修改)
服务端组件
React Server Component 服务器组件 ------ 运行在服务器端的组件。
不用管这些概念和术语,因为你已经开始使用服务端组件了,上文的三个页面,都是服务端组件 ------ 不知不觉就让你用上了 ------ 这就是简单的技术和框架,随风潜入夜,没有任何门槛。
为了能更好的体现出"运行在服务端",我们可以继续加一些功能。让我们修改一下 src/app/blogs/[id]/page.tsx
这个页面
首先,给组件函数增加 async
,然后创建一个函数 MockGetBlogDetailFromDataBase
模拟从数据库获取博客详情,最后在组件函数中调用获取数据,并在组件 JSX 中返回。
js
// 服务端组件,在 node 服务端环境执行(无法在客户端浏览器执行)
// 模拟从数据库获取博客详情
async function MockGetBlogDetailFromDataBase(id: string) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id,
title: `博客标题${id}`,
content: '博客详情 博客详情 博客详情',
})
}, 1000)
})
}
export default async function OneBlog({ params }: { params: { id: string } }) {
const blog = (await MockGetBlogDetailFromDataBase(params.id)) as any
return (
<div>
<h1 className="text-3xl">{blog.title}</h1>
<p>{blog.content}</p>
</div>
)
}
以上这些代码,async function
查询数据库,只能在服务端去执行,只能服务端组件去做。最后执行的结果也是显而易见的
先不要什么概念、好处、区别啥啥的,就说上面这些代码和逻辑,你能不能理解?能的话继续,不能给我留言评论。
客户端组件
纯前端使用 React 开发的就是客户端组件,即代码在客户端(浏览器)执行。客户端组件不能使用 async function
更不能去直接查询服务端数据库。
Next.js 中默认组件是 Server Component 服务端组件,如果你想使用客户端组件,必须给一个标志 'use client'
让 Next.js 知道你的意图。下面的 demo 中有。
有点麻烦啊,为啥要用客户端组件呢,多使用服务端组件不好吗?------ 当然不行,因为服务端组件有很多限制。
当你使用 useState
useEffect
onClick
等 API 时,即当你需要有一些用户交互,需要使用浏览器 API ,需要有组件的更新和变化时,就需要客户端组件。
服务端组件就是一个"直肠子"直接输出,没那么多小动作。
例如,我们修改一下 src/app/blogs/page.tsx
页面,显示 Blog 列表,再增加"删除"功能。 其实这些代码都是非常基础的纯前端 React 代码,大家不看也罢。
tsx
'use client'
import { useState } from 'react'
export default function Blogs() {
const [blogs, setBlogs] = useState([
{ id: 1, title: 'Blog 1', content: 'Content 1' },
{ id: 2, title: 'Blog 2', content: 'Content 2' },
{ id: 3, title: 'Blog 3', content: 'Content 3' },
])
function handleDelete(id: number) {
setBlogs(blogs.filter((blog) => blog.id !== id))
}
return (
<div>
<h1 className="text-3xl">Blog List</h1>
<div>
{blogs.map((blog) => (
<div key={blog.id} className="border p-2 my-2 relative">
<h2 className="text-xl">{blog.title}</h2>
<p>{blog.content}</p>
<button
className="underline absolute top-4 right-3"
onClick={() => handleDelete(blog.id)}
>
删除
</button>
</div>
))}
</div>
</div>
)
由于用到了 useState
onClick
所以必须使用客户端组件,代码第一行标记 'use client'
。
创建一个服务端 API
客户端组件没办法直接查询服务端数据库,那就得使用 axios 或 fetch 等去 ajax 请求服务端数据,这些是前端 JS 的基本能力。
Next.js 作为 SSR 服务端框架,它也提供了开发服务端 API 的能力。新建一个目录 src/app/api/blogs
在其中新建文件 route.ts
,然后输出一个 GET
方法,返回一段 JSON
浏览器访问 http://localhost:3000/api/blogs
即可看到效果。
和新建页面一样,这都是 Next.js 路由规则。页面使用 page.tsx
文件,API 使用 route.ts
文件。支持 GET
POST
PATCH
DELETE
等所有 HTTP method 。
js
export async function GET() {
/* 省略已有代码 */
}
export async function DELETE(id: string) {
// 在数据中删除 id 对应的 blog ...
return Response.json({ errno: 0 })
}
有了服务端 API ,刚才的客户端组件也可以使用 axios 或 fetch 来请求数据了,这个太基础了,就不再演示了。
Server Action
无论是服务端组件,还是客户端组件,输出的都是 UI ,都可以和用户交互。当用户点击一个按钮,或者提交一个表单,都需要提交数据到服务端。
纯前端考虑的到的方案,就是 ajax POST 到服务端。现在 Next.js 给了你一个更简单的选择 ------ Server Action ------ 直接调用一个函数即可(让你忘掉 ajax) 。当然了,如果你不喜欢,可继续用 ajax POST 。
对于服务端组件,可通过 'use server'
定义一个函数为 Server action 然后直接调用。
对于客户端组件,需要你单独创建一个 JS 文件,标记为 'use server'
,并 export 一个函数
ts
// src/blogs/action.ts
'use server'
export async function delBlog(id: number) {
// 调用数据库,删除 id 对应的 blog
}
然后在客户端组件 import 这个函数,直接调用即可。
PS:虽然写法上是一个函数调用,但 Next.js 还是会把它处理为一个 POST 请求,所以它只是一个语法糖,方便我们学习和使用。
另外,Server action 用的比较多的地方还有 form ,但我觉得不如上面两个例子来的直接一些。
你已经入门了
恭喜你,能看到这里,你已经入门了,核心概念就这些。你完全可以去做一个增删改查的 demo 。文本 demo 的源代码 github.com/wangfupeng1...
虽然还有很多 Next.js 高阶应用和优化方案没有讲到,但那些都是锦上添花的东西,去看文档查找即可。
接下来强烈建议你再去看看 Next.js Learn 页面,跟着一步一步做出这个小项目。我觉得这是讲解最好的 Next.js 入门项目,毕竟是官方出品。英语不熟练的可以直接浏览器翻译。
加入【划水AI】项目研发团队
如果你想进一步学习 Next.js 框架,如 shadcn-ui 、登录校验、ORM、数据库、Serverless 、线上统计和监控等。欢迎加入 划水AI 项目研发小组。
除此之外,我们还会深入研发富文本编辑器、协同编辑、AIGC 能力(AI 写作、AI 文字处理、AI 智能提示等)。有兴趣私聊我~