Next.js 全栈博客开发记

힘센캥거루
2026년 1월 13일(수정됨)
4
8

在第一次接触网页开发大约 1 年之后,我开始想着想要拥有一个属于自己的博客。

于是我就大概花了 6 个月几乎只埋头做这件事。

Next.js 全栈博客开发记-1

前端部分的功能,参考金度亨先生的博客就已经足够了。

我用 mdx 搭建起一个博客,其实连一周都没花到。

事实上,仅靠前端也并不会给博客运营带来什么大问题。

有一阵子我也觉得,只要写 mdx,然后做前端开发就已经够用了,但还是放不下“无论在哪都能连上网写文章的全栈博客”这个梦想。

1. 功能

功能其实没什么特别的。

可以发文章,也可以查看文章。

Next.js 全栈博客开发记-2

登录之后,右上角的按钮形态会发生变化。

点击之后就可以上传文章。

从下面的视频可以看到写文章和上传的过程。

有一个图片转换的流程,所有上传的图片都会以效率最高的 avif 格式进行管理。

YouTube 只要粘贴链接,就可以像上面那样插入。

表格管理功能的想法是从 Tistory 得到的。

Next.js 全栈博客开发记-3

当焦点落在表格上时,上方会弹出一个可以新增或删除表格的工具栏。

当然也支持临时保存。

在编辑器中按 cmd+s 或者 ctrl+s 就会保存。

2. 架构

“架构”这个词听起来很酷,用韩语说就是“结构”。

最先进入服务器的请求会由 Caddy 作为反向代理进行分发。

如果只有前端的话,就会直接把 mdx 转成页面后发送出去,但既然已经做了后端,就用 Next.js 和 Prisma 把两边连了起来。

Next.js 全栈博客开发记-4

大家都知道,如果把来自用户的数据不加处理直接塞进后端,很快就会被黑掉。

一定要通过 API 验证之后再写入。

Next.js 全栈博客开发记-5

有一次我希望博客的评论能多一点,于是就把 API 对所有人开放,谁都可以发表评论。

结果有家伙不断地通过那个 API 发起 SQL 注入攻击,评论居然堆到了大概 100 条。

也有人在 svg 图片内部插入 fetch 和命令,试图进行黑客攻击。

说不定此刻在中国某个地方,有一个我自己都不知道的“另一个我”正好端端地走来走去也不一定。

3. 编辑器

Next.js 全栈博客开发记-6

我花最多心思做的就是编辑器。

编辑器是在 Tiptap 的基础上叠加了各种功能制作而成的。

因此光是和 Tiptap 相关的组件就超过了 10 个。

Next.js 全栈博客开发记-7

以插入图片为例,是像下面这样实现的。

  const editor = useEditor({
    extensions: editorExtensions,
    content,
    onUpdate: ({ editor }) => {
      onChange(editor.getHTML());
    },
    editorProps,
    immediatelyRender: false, 
  });

  const { uploadProgress, uploadImage, clearProgress } = useImageUpload({
    onUploadComplete: (imageUrl, isAnimated) => {
      if (editor) {
        try {
          editor
            .chain()
            .focus()
            .insertContent([
              {
                type: "image",
                attrs: {
                  src: imageUrl,
                  alt: "업로드된 이미지",
                  "data-animated": isAnimated || false,
                },
              },
              {
                type: "paragraph",
                content: [],
              },
            ])
            .run();
        } catch (error) {
          console.error("이미지 삽입 오류:", error);
        }
      }
    },
  });

为了在性能方面占优势,我直接保存并加载 HTML。

从 Tiptap 往后端保存时,我在中间加了不少处理流程。

对代码块、Google 地图、标题、图片、链接预览等做了各种预处理,然后把 content 和 processedContent 分开保存。

4. 后端 Schema

最近在做多语言服务的时候,我意识到,一旦改动已经做好的后端 Schema,就几乎是灾难。

突然又想起了那位总强调 Schema 重要性的教授说的话。

如果你也打算做到博客后端这一层,希望你能花很长时间思考要提供什么样的服务。

东加一点西加一点,结果 Schema 变得太臃肿了,虽然有点羞愧,但还是公开出来。

model Post {
  id               String          @id @default(cuid())
  title            String
  slug             Int             @unique @default(autoincrement())
  excerpt          String
  content          String
  processedContent String?         
  thumbnail        String?
  published        Boolean         @default(false)
  createdAt        DateTime        @default(now())
  updatedAt        DateTime?
  likes            Int             @default(0)
  views            Int             @default(0)
  comments         Comment[]
  likesRows        PostLike[]
  viewDedups       PostViewDedup[]
  notifications    Notification[]
  locales          PostLocale[]
  author           User            @relation(fields: [authorId], references: [id], onDelete: Cascade)
  authorId         String
  category         String
  @@map("posts")
}

5. 后记

现在该来回答下面这个问题了。

做博客的时候,有必要做到后端吗?

全部做完之后,我脑子里冒出的念头是:何必呢?

到头来我才意识到,WordPress 才是那条非常轻松的道路

一旦开始实现后端和 API,要考虑的事情就会急剧增多。

最近在 react2shell 事件期间,攻击几乎是以分钟为单位涌过来,终端里不断出现从来没见过的命令,有好几天都很煎熬。

即便是现在,我也还是每隔几天就用 npm audit 检查并修复安全问题。

偶尔改 DB Schema,迁移一旦出错就会冷汗直冒。

不过回头看,通过实现后端我确实学到了很多东西,而且拥有了一个可以亲手实现功能的博客,这一点还是挺不错的。

接下来我不打算再给博客添功能了,更多是想通过做其他项目来让自己的作品集更加充实。

관련 글

使用 Caddy 实现 Next.js 无停机部署(本地服务器)
使用 Caddy 实现 Next.js 无停机部署(本地服务器)
每次一有想在主页上加点什么的念头就去 build,结果中间好像偶尔会有人访问。于是 Search Console 上的分数开始一点点往下掉。觉得这样不行,就开始思考要怎么做无停机部署。1. 两个项目文件夹 + 两个终端答案出乎意料地简单:开两个终端。在一个终端里 build,另一个终端里让服务器跑着...
谷歌搜索索引自动化 - Web Search Indexing API
谷歌搜索索引自动化 - Web Search Indexing API
继上次搞完 IndexNow 之后,也决定在谷歌这边做一下自动化。查了一下,发现谷歌是通过一个叫 Web Search Indexing 的 API 来支持这个功能的。1. 适用范围官方文档中,这个 API 正式支持的范围是招聘公告和流媒体视频服务。说是为了给对实时性要求高的内容创建索引用的,但搜了...
搜索索引生成自动化 - IndexNow
搜索索引生成自动化 - IndexNow
在向 Bing 提交站点收录时才发现,Bing 提供了一个叫做 IndexNow 的功能。核心在于,可以利用 API Key,在写完文章的瞬间就立刻发起索引请求。只要用 fetch 写出类似下面这样的请求,并把它串联到「写文章」流程里,就可以在把文章保存到 DB 的同时发送索引生成请求。POST /...
被入侵的 Nextjs、React 服务器经历
被入侵的 Nextjs、React 服务器经历
我最初接触到这次安全问题是在 12 月 5 日凌晨。据说在 React 中可以在未认证的情况下进行远程代码执行。看到这条新闻后,我虽然告诉了别人,但总觉得自己应该没事,就什么想法也没有地略过了。1. 发现被入侵的痕迹结果当我想登录更新博客代码时,在终端里发现了有命令被执行过的痕迹。/bin/sh:...
在博客中添加多语言功能(NextJS、next-intl、Vercel AI SDK)
在博客中添加多语言功能(NextJS、next-intl、Vercel AI SDK)
最近我觉得博客需要多语言功能。于是决定用 next-intl 来实现多语言服务。1.i18n首先,在做多语言服务时有一些必须遵守的原则。这被称为 internationalization,单词太长,所以把首字母 i 和尾字母 n,以及中间 18 个字母合在一起,写成 i18n。1) i18n 的原则...
配置 Nextjs 的多语言支持
配置 Nextjs 的多语言支持
为了增加我的博客的访问量,我考虑尝试设置多语言。找到了多种方法,首先的一步是为语言设置不同的路由。决定使用 next-intl 来实现。1. 安装 yarn add next-intl 2. 应用...

댓글을 불러오는 중...