bgm004'blog

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

我的博客重构之旅

2024-05-25
React
Nextjs

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

技术选型

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

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

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

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

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

为什么不是Nuxt?想尝试React,而且我也没多到Next.js有这么多bug。

数据源

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

文章列表,Atomsitemap都是通过使用fs遍历_posts下全部文件夹获取数据。为了避免每次生成页面时重复读取全部文章获取时间进行排序,我在项目启动前读取全部文章的 Frontmatter 数据,进行排序并存储在项目中,减少对_posts目录的遍历次数。

由于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}
    />
  )
}

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

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

为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实现图片放大预览。