bgm004'blog

一个二次元宅男的无聊日常

我的博客重构之旅

2024-05-25
React
Nextjs

咕咕了好几年,我的博客终于要重构上线了!

技术选型

以前用的Hexo已经太过于古老想换个现代化的框架。热门选择基本都看了一下。

  • VuePress / VitePress 这两个框架构建的博客都带有浓厚的文档风格,与我期望的博客风格不符。(不知道是谁的问题😅)
  • Nuxt.js: 当时 Nuxt.js 刚开始适配 Vue3,存在不少 bug,而且我对 SSR 还不熟悉,因此放弃了。
  • Gatsby/gridsome  这两个框架的数据源只能是 graphqlgridsome生态比前者更差。
  • Next.js 支持 SSG 和静态导出,对数据源没有限制,而且只要略懂 React 语法就能轻松上手。(13版本最开始的时候bug也很多)
  • Astro 支持多种框架,这一点非常赞,但是我并不喜欢它的语法。

Vercel 推出的 turbopack 我一直到 Next.js 14.2.3 版本都无法正常使用,遇到了各种报错。希望明年能够顺利使用它,webpack 实在是太慢了。

博客评论功能使用的是 walien,基于 Solidjs 构建的客户端。压缩后仅有 14kb,修一波bug后会开源。

最终方案为: Next.js + TypeScript + MDX + TailwindCSS

数据源

_posts --------------------- // 文章目录
  ├─slug ------------------- // 每篇文章一个文件夹
  │ ├─index.md ------------- // 文章
  | |-components.ts -------- // 文章中使用的组件
  | |- * ------------------- // 图片等资源

由于 next-mdx-remote 不支持在文章中使用 import,因此只能在单独的文件中导入组件并传递给 MDXRemote

// _posts/refactor-my-blog-using-nextjs/components.ts
import BgmSubject from '@/components/BgmSubject'
export { BgmSubject }

// app/posts/[slug]/page.tsx
export default async function Page({ params }: props) {
  let postComponents = {}
  try {
    postComponents = await import('@/_posts/${params.slug}/components')
  } catch (e) {}
 /* ... */
  return (
    <MdxRemote
      source={post.content}
      components={postComponents}
    />
  )
}

为了避免每次生成页面时都读取全部文章以获取时间进行排序,我在项目启动前读取全部文章的 Frontmatter 数据,进行排序并存储在项目中,减少对 _posts 目录的遍历次数。

首页内容原本打算使用 memos 管理,但由于没有服务器无法使用 Docker 部署,只能由 Leacloud 代替。编写了一个简单的页面进行发布和管理,更新数据后调用 Next.js 的 revalidatetag 刷新首页数据。

bangumi页面使用bangumi的api获取数据,由于数据太多又懒得搞分页没必要,所以只展示前21条数据。二次元老婆部分用爬虫,因为官方api不能获取目录中收藏的角色。历史遗留问题

为markdown赋能

在 Hexo 时代,为 Markdown 生成的内容添加交互操作十分繁琐,而现在借助 MDX 的能力,只需定义组件替换 DOM 元素即可实现,无需额外学习成本。如果不使用 MDX,一些原本可以在服务端完成的功能就需要在客户端处理,添加交互也难以像组件化那样方便管理代码。

Next.js 集成的 MDX 功能不够灵活,仅适用于文档站点。

对于基本的 Markdown 内容,mdx-bundler 的输出至少比 next-mdx-remote 的输出大 400%。(RSC加持下这一优势我不确定是否还存在)。由于我在 Markdown 中使用组件的频率较低,因此我选择使用 next-mdx-remote

我只需要在文章中写<BgmSubject id="395378" />即可实现如下效果。直接贴链接也可以做到但是还没有弄。写这篇文章的时候才想到

迷宫饭
迷宫饭
迷宫饭
TV
迷宫饭,不是吃就是被吃… 妹妹在迷宫深处被赤龙吃掉了! 冒险者莱欧斯侥幸逃过一命回到了地面。 他想要再度挑战迷宫,但是钱和食物都留在了迷宫深处… 在妹妹随时可能会被消化掉情况下,莱欧斯下定了决心: 「食物要在迷宫内就地取材」 史莱姆、鸡尾蛇、宝箱怪、然后是龙! 冒险者啊,一边吃掉袭来的魔物,一边通关迷宫吧!

默认元素我只替换了preimg。使用shiki实现代码高亮,为代码块添加复制按钮。plaiceholder配合next/image优化图片,react-medium-image-zoom实现图片放大预览。