从零看懂一个 Next.js 笔记应用

一、这个项目到底是什么?

打开目录,你会看到一张"全家福":app(页面)、components(组件)、lib(数据层)、public(静态资源),外加一个 package.json。它们共同组成了一个笔记系统------用户可以新建、查看、编辑、删除笔记,笔记内容支持 Markdown 格式,界面左边是笔记列表、右边是笔记正文。

用一句话概括:这是一个"全栈"(前后端一体)的 Web 应用。它不需要单独再写一个后端服务器,因为 Next.js 把"网页展示"和"数据读取"这两件事合并到了一个项目里。

在动手之前,先建立两个概念,后面所有内容都会围绕它们展开:

  • 前端:浏览器里用户看得见、点得到的东西(列表、按钮、文字)。
  • 后端:藏在服务器里,负责"取数据、存数据"的逻辑。

传统的做法是前后端分离,用两个项目、两套语言。而这个项目里,Next.js 一个框架就全包了。


二、Next.js 是什么?------给 React 装上"发动机"

如果你学过一点点前端,一定听过 React 。React 的核心思想是组件化:把界面拆成一个个可以复用的"积木块"(组件),每个积木块负责渲染一部分画面,数据一变,画面就自动更新。

但"光有 React"其实是不够的。React 本身主要管"在浏览器里渲染界面",它有几个天生短板:

  1. 首屏慢:如果所有内容都要等浏览器下载完 JS 再渲染,用户会看到白屏。
  2. 搜索引擎看不懂:百度、Google 的爬虫喜欢"直接能读到的 HTML 文字",而纯前端渲染出来的页面常常是一片空的 HTML 壳子,爬虫抓不到内容,**SEO(搜索引擎优化)**就很差。
  3. 没有约定好的路由:页面跳转、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.jscomponents/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,而是选了 Redislib/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。这是解决"数据库读写瓶颈"的常用手段。


六、一次请求的"旅行":数据是怎么流动的

把上面的碎片拼起来,就能看到一次完整的流程。假设用户访问首页,发生了这些事:

  1. 请求进入 → 命中根布局 app/layout.js
  2. layout.js 渲染 <Sidebar/>(侧边栏组件)。
  3. Sidebar 是服务端组件,它 await getAllNotes(),去 Redis 取出所有笔记。
  4. Sidebar 把数据传给 <SidebarNoteList notes={notes}/>
  5. SidebarNoteListObject.entries(notes) 把"哈希对象"转成数组,再用 .map() 把每一条笔记渲染成一个 <SidebarNoteItem/>(列表项)。
  6. 每个列表项解析 JSON、用 dayjs 格式化日期、截取正文前 20 个字作为摘要。
  7. 最终,服务器把所有内容拼成一份完整的 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-excerptnote--empty-state 这类名字,这是 BEM 规范

  • Block(块) :独立的部分,如 notesidebar
  • Element(元素) :块里的子元素,用 - 连接,如 note-text
  • Modifier(修饰符) :表示状态或变体,用 -- 连接,如 note--empty-state("空状态"的 note)。

一套统一的命名,能让团队协作时"一看名字就知道这是哪块、什么状态"。

3. 组件拆分

侧边栏被拆成了 SidebarSidebarNoteListSidebarNoteItemSidebarNoteItemContent 这样层层递进的小组件。组件越小、职责越单一,越容易理解和复用。这正是 React 组件化思想的精髓。


八、藏在 <head> 里的 SEO 秘密

app/layout.js<head> 里写了 titledescriptionkeywords

html 复制代码
<title>戴总的LLM工程师博客</title>
<meta name="description" content="..."/>
<meta name="keywords" content="llm,claude,deepseek,rag,langchain"/>

这看起来只是几行 HTML,却是 SEO 的关键:搜索引擎爬虫靠这些标签来理解"这个页面讲什么"。再配合前面说的 SSR(服务端渲染,内容直接存在于 HTML 中),爬虫就能顺利读到正文,网站也就更容易被搜到。这就是"技术栈选择"和"业务目标"之间微妙的联系。

相关推荐
cindershade1 小时前
从零实现画布「双击创建节点」:一个 VueFlow 项目的交互全记录
前端
YHL1 小时前
🚀 SSE 服务器发送事件与 BFF 层实战
前端·后端
今日无bug1 小时前
HTML5 Canvas:从画图到游戏开发
前端·canvas
cindershade1 小时前
用 Web Workers 优化前端重计算任务:避免主线程卡顿的实战方案
前端
爱丶不疚1 小时前
搞不清 CommonJS 与 ESM,你是否也有这些疑问🤔
前端·node.js
YIAN1 小时前
http无状态?State来展示!从基础路由到鉴权守卫,吃透 SPA 前端路由核心
前端·react.js
__zRainy__1 小时前
Node系列 · Node基础:全局变量与全局对象
开发语言·前端·javascript
码云骑士1 小时前
104-实战论文搜索引擎-ArXiv爬取-Milvus存储-RAG问答-Gradio前端
前端·python·搜索引擎·milvus
sunly_1 小时前
TypeScript总结:15、类型速查
前端·javascript·typescript