一、这个项目到底是什么?
打开目录,你会看到一张"全家福":app(页面)、components(组件)、lib(数据层)、public(静态资源),外加一个 package.json。它们共同组成了一个笔记系统------用户可以新建、查看、编辑、删除笔记,笔记内容支持 Markdown 格式,界面左边是笔记列表、右边是笔记正文。
用一句话概括:这是一个"全栈"(前后端一体)的 Web 应用。它不需要单独再写一个后端服务器,因为 Next.js 把"网页展示"和"数据读取"这两件事合并到了一个项目里。
在动手之前,先建立两个概念,后面所有内容都会围绕它们展开:
- 前端:浏览器里用户看得见、点得到的东西(列表、按钮、文字)。
- 后端:藏在服务器里,负责"取数据、存数据"的逻辑。
传统的做法是前后端分离,用两个项目、两套语言。而这个项目里,Next.js 一个框架就全包了。
二、Next.js 是什么?------给 React 装上"发动机"
如果你学过一点点前端,一定听过 React 。React 的核心思想是组件化:把界面拆成一个个可以复用的"积木块"(组件),每个积木块负责渲染一部分画面,数据一变,画面就自动更新。
但"光有 React"其实是不够的。React 本身主要管"在浏览器里渲染界面",它有几个天生短板:
- 首屏慢:如果所有内容都要等浏览器下载完 JS 再渲染,用户会看到白屏。
- 搜索引擎看不懂:百度、Google 的爬虫喜欢"直接能读到的 HTML 文字",而纯前端渲染出来的页面常常是一片空的 HTML 壳子,爬虫抓不到内容,**SEO(搜索引擎优化)**就很差。
- 没有约定好的路由:页面跳转、URL 结构需要自己搭。
Next.js 就是来解决这些问题的。它建立在 React 之上,提供了一整套"开箱即用"的能力,其中最重要的两个词是:
- SSR(Server-Side Rendering,服务端渲染) :页面在服务器上就渲染成 HTML,再发给浏览器。这样首屏快,爬虫也能直接读到内容。
- RSC(React Server Component,服务端组件):一部分组件直接在服务器上运行,不需要把代码下载到浏览器。这是 React 19 里的一个重大升级。
可以这样打比方:React 是"车架",Next.js 是"整车"。你买的是整车,但它内部还是那套车架在支撑。
三、文件即路由:App Router 的魔法
这个项目最"反直觉"的地方在于:你几乎看不到路由配置文件 。页面是怎么组织起来的呢?答案藏在 app 目录的文件夹结构里。这就是 Next.js 的 App Router(应用路由) ,它的核心规则是:文件夹的名字就是网址的路径,文件的名字决定这个路径干什么。
对照一下真实的目录结构:
bash
app
├── layout.js ← 全局布局(每页都套用)
├── page.js ← 首页 "/"
├── note
│ ├── [id]
│ │ └── page.js ← 笔记详情页 "/note/123"
│ └── edit
│ ├── page.js ← 新增笔记页 "/note/edit"
│ └── [id]
│ └── page.js← 编辑笔记页 "/note/edit/123"
几个关键点:
page.js代表"一个真正的页面",访问对应路径时,Next.js 就渲染这个文件。layout.js是"外壳",它包住所有子页面,负责放那些每页都一样的部分(比如侧边栏、网站的<title>、<meta>)。[id]是动态路由 :方括号表示"这一层是变量"。比如note/[id]/page.js能同时匹配/note/1、/note/2、/note/abc......每个不同的id就是一条不同的笔记。
用一句话记住:以前是"先写路由表,再写页面";现在是"文件夹怎么摆,路由就怎么定"。 这种"约定大于配置"的思路,让项目结构一目了然,新人看目录就能猜到网址。
四、服务端组件 vs 客户端组件:一场"分工革命"
这是本项目最有意思、也最值得初学者理解的部分。React 19 把组件分成了两种"工种":
1. 服务端组件(默认)
看 app/layout.js 和 components/Sidebar.js,你会发现它们的函数前面有个 async,而且可以直接用 await 去读数据库:
js
export default async function Sidebar(){
const notes = await getAllNotes(); // 在服务器上直接查 Redis
...
}
这就是服务端组件 :它在服务器上运行 ,可以直接访问数据库、读文件系统,然后把结果渲染成 HTML 发给浏览器。好处是:敏感逻辑和数据查询留在服务器,浏览器只拿到结果,又快又安全。
2. 客户端组件(需要交互)
再看 components/SidebarNoteItemContent.js,文件第一行写着 "use client":
js
"use client";
import { useState, useEffect } from "react";
加了这行,就告诉 Next.js:"这个组件要在浏览器 里跑"。因为它将来会用到 useState(管理状态)、useEffect(副作用)这类只有浏览器端才有的交互能力,比如点击展开、实时搜索。
一句话总结分工:
- 能放在服务器上做的(读数据、渲染静态内容)→ 服务端组件,默认即可。
- 必须让用户交互的(点击、输入、动画)→ 客户端组件,标
"use client"。
这种"默认服务端、按需客户端"的设计,正是 Next.js 性能优异的核心秘密之一。
五、数据从哪来?------Redis:像"超快记事本"的数据库
有了页面,还要有"装笔记的地方"。这个项目没有用常见的 MySQL,而是选了 Redis 。lib/redis.js 是整个项目的数据层。
Redis 是一种 NoSQL(非关系型)内存数据库。怎么理解它?
- 关系型数据库(如 MySQL):像"表格",有行有列,要先设计表结构,用 SQL 语句查询。
- Redis :更像一个巨大的、放在内存里的"键值对"字典 。你给它一个 key(钥匙),它立刻还你一个 value(值)。因为数据全在内存里,所以快到飞起。
项目里的数据长这样:
js
const initialData = {
"1702459181837": '{"title":"sunt aut","content":"...","updateTime":"..."}',
...
}
每一行就是一个笔记:key 是笔记的 ID(一串数字),value 是一个 JSON 字符串,里面装着标题、正文、更新时间。
Redis 还特别聪明:针对不同数据类型有专门的处理方式。这里用的是 hash(哈希)类型 ,可以把"整个笔记集合"当成一个叫 notes 的大哈希表,用 hgetall('notes') 一次性取出所有笔记,用 hset('notes', ...) 存进去。
js
export async function getAllNotes(){
const data = await redis.hgetall('notes'); // 取全部
if(Object.keys(data).length == 0){
await redis.hset("notes", initialData); // 空就塞初始数据
}
return await redis.hgetall("notes");
}
Redis 在真实项目中还有个经典用途------缓存:把经常被读、但很少变化的数据(比如文章列表)先存一份在 Redis,用户访问时直接从内存拿,不用每次都去慢吞吞地查 MySQL。这是解决"数据库读写瓶颈"的常用手段。
六、一次请求的"旅行":数据是怎么流动的
把上面的碎片拼起来,就能看到一次完整的流程。假设用户访问首页,发生了这些事:
- 请求进入 → 命中根布局
app/layout.js。 layout.js渲染<Sidebar/>(侧边栏组件)。Sidebar是服务端组件,它await getAllNotes(),去 Redis 取出所有笔记。Sidebar把数据传给<SidebarNoteList notes={notes}/>。SidebarNoteList用Object.entries(notes)把"哈希对象"转成数组,再用.map()把每一条笔记渲染成一个<SidebarNoteItem/>(列表项)。- 每个列表项解析 JSON、用
dayjs格式化日期、截取正文前 20 个字作为摘要。 - 最终,服务器把所有内容拼成一份完整的 HTML,返回给浏览器,用户看到左侧笔记列表。
这条"数据流"可以总结为一句口诀:
layout(骨架)→ 服务端组件取数据 → 组件层层传参 → 渲染成 HTML`
理解这条链路,你就理解了全栈 Web 应用最基本的运行原理。
七、让代码更好维护:三个"工程化"习惯
除了核心功能,这个项目还示范了几个优秀工程师的日常习惯,值得留意:
1. 路径别名(alias)
看 jsconfig.json,它配置了 @/components/* 和 @/lib/*。这意味着写代码时,你可以用:
js
import Sidebar from '@/components/Sidebar' // 简洁
而不必写成:
js
import Sidebar from '../../../components/Sidebar' // 又长又易错
@ 直接指向项目根目录。好处 :路径变短、不怕文件挪位置后一堆 ../../ 算错。
2. BEM 命名规范
CSS 的 class 名不是随便起的。项目里能看到 sidebar-note-excerpt、note--empty-state 这类名字,这是 BEM 规范:
- Block(块) :独立的部分,如
note、sidebar。 - Element(元素) :块里的子元素,用
-连接,如note-text。 - Modifier(修饰符) :表示状态或变体,用
--连接,如note--empty-state("空状态"的 note)。
一套统一的命名,能让团队协作时"一看名字就知道这是哪块、什么状态"。
3. 组件拆分
侧边栏被拆成了 Sidebar → SidebarNoteList → SidebarNoteItem → SidebarNoteItemContent 这样层层递进的小组件。组件越小、职责越单一,越容易理解和复用。这正是 React 组件化思想的精髓。
八、藏在 <head> 里的 SEO 秘密
app/layout.js 的 <head> 里写了 title、description、keywords:
html
<title>戴总的LLM工程师博客</title>
<meta name="description" content="..."/>
<meta name="keywords" content="llm,claude,deepseek,rag,langchain"/>
这看起来只是几行 HTML,却是 SEO 的关键:搜索引擎爬虫靠这些标签来理解"这个页面讲什么"。再配合前面说的 SSR(服务端渲染,内容直接存在于 HTML 中),爬虫就能顺利读到正文,网站也就更容易被搜到。这就是"技术栈选择"和"业务目标"之间微妙的联系。