· 建设

使用 Astro 重建博客

本博客的过去、现在与未来。

动态博客

这一类的代表有 WordPress、Typecho 等。本博客在 2015-2017 年使用过 Typecho。

动态博客需要使用后端服务器,这会引入后端执行的开销曾经写过一个让后端每次响应请求时对所有多级分类所有文章计数的 Typecho 模板,页面加载时间 1s+。并且当时使用的是虚拟主机,性能堪忧,后来主动迁移到了静态博客体系。

Hexo

静态博客不需要后端服务器,所有内容先生成再部署。这样如果只需要搭建博客的话,可以省下服务器的费用。这是本博客从 2018 年起使用的方案。

但是,Hexo 已经有超过 10 年的历史了,其使用的模板是基于 HTML 字符串的拼接,没有组件抽象不会真有人使用 Web Component 吧?,也不能整合现代的前端工具链。后来随着个人事务愈加繁忙,没有大块时间维护博客,逐渐废弃。

自制博客框架

2023 年暑假之后,重建博客事宜提上日程。首先的想法是自己基于现有的 Web 框架、前后端都由自己从零开始搭建,并在 2023 年 8 月至 2024 年 9 月期间迭代过多个版本,其中迭代过的技术栈包括:

  • Django 框架实现的前后端
  • Rust Rocket 实现的 SSR(前后端实现为同一个端口上的服务,但逻辑上将前后端代码分成两个 mod
  • React + Flask 实现的前后端分离

但是最终都失败了。原因包括:

  1. 自建后端的安全性问题。很难保证自己手写的权限认证是 bug-free 的(很容易在哪个地方忘记加权限控制,成了系统的突破口)。
  2. 前后端分离的问题。前端必须调用后端提供的 API 获取博客数据,然后再渲染在页面上。在 API 返回并渲染前存在一个时间差,用户会看到什么都没有的空壳网页。另外前后端分离的 SEO 优化问题也无法忽略,如果用 SSR 则又会引入更大的服务器性能开销。
  3. 很可能需要先部署博客,然后再考虑购置服务器。这样就存在一段必须使用静态网页的过渡期,在此期间无法部署后端。

Next.js

到这里,基本已经确定了静态博客的技术路线。考虑到上半年我为北大 POA 举办的城市定向活动的线上系统编写了系统后台的前端,该前端是用 React 框架实现,因此首先看中了基于 React 的 Next.js。但是我的前端功底还是不太够……我没能将半成品 React 版前端顺利迁移过来。

Astro

Astro 号称是「为内容驱动的网站设计的 Web 框架」,并且有内容集合、基于文件的路由等适合静态博客的特性。而且它支持基于 remark.js 的 Markdown 工具链。这些设计使得基于 Astro 搭建博客前端更为容易(但代价是 all in SSR/SSG,用 Astro 写传统的 SPA 会很难受)。你现在看到的博客就是基于 Astro 设计的。

不过,虽然 Astro 的大多数特性很好,但是仍然有一些残留问题有待解决(几乎都是关于 Markdown 渲染的):

  1. Astro 只能对整篇文章进行渲染。但是首页的文章列表只需要显示每篇文章的摘要。在许多博客系统(如 Typecho, WordPress, Hexo)中可以通过 <!-- more --> 标签表示该标签前面的内容为本篇文章的「摘要」,但这只是约定俗成,并没有任何标准规定。因此,为了在 Astro 页面中显示摘要,首先必须自己解析摘要标签、然后自己将摘要的 Markdown 源代码编译为 HTML、最后显示在页面上1。好在 Astro 提供了 <Fragment> 标签可以插入原始 HTML 代码,这样至少可以自己手写一个 remark 编译组件来显示摘要。
  2. 很难用组件呈现我自己的扩展语法需求。我设计了一些特殊标记/组件,可以标记文字颜色、插入黑幕、插入 GitHub 仓库 / Bilibili 视频卡片等。在 React 中,可以使用 react-markdown 组件,它在一般的 remark.js 工具链的基础上提供了标签映射功能(将 HTML 标签映射为 JSX 组件),这样可以方便地实现上述特殊功能。在 Vue 中也有 remark-vue 库可以支持这些需求,但是在 Astro 中没有这样的组件。
  3. 实际上也不是完全没有办法在 Astro 的 Markdown 中插入扩展组件,可以通过 MDX 实现。但是 MDX 有许多不兼容 Markdown 的语法设计(不支持 HTML 注释 <!-- --> 从而也不支持 <!-- more --> 摘要标签、必须在正文中使用 import 语句显式导入组件等),这样就需要在 Markdown 渲染之外再手动实现一个 MDX 渲染器。尽管有 remark-mdx@mdx-js/mdx 等库,我仍然不知道如何将编译后的 MDX 转换为 Astro 或其他我自己编写的 JSX 组件2。即便不考虑这些,MDX 和 Markdown 的语法兼容性问题已经足够让我放弃这种想法。另外我也不需要 MDX 提供的动态功能。

还有一些不那么大的问题:

  • 没有 RSS 摘要功能。这跟上面说的 Astro 只能渲染整篇文章其实是一样的问题。通过自己实现 Markdown 解析器,可以支持摘要提取功能,这样就能填充进 RSS 了。
  • Next.js 并没有内置 Markdown 支持,但是很讽刺的是 Astro 自带的 Markdown 支持其实也不够用……

目前博客前端的 Markdown 处理比较折腾:我自己写了一个基于 remark.js 的渲染器,用它替换掉了 Astro 自带的渲染功能;但是自己写的渲染器没有办法支持组件,因此带有特殊组件的文章不得不用 Astro 自带的 MDX 集成……

杂谈

也许未来我会考虑把博客整体用 Next.js 重新实现一遍,这样就只需要一套渲染器了。在必须自己实现 Markdown 渲染的前提下,Astro 其实引入了维护两套渲染器的负担。但是 Astro 用起来真的很简单

Next.js 也不是完美满足一切要求的框架,比如它的组件样式写法相当不优雅——但这几乎是所有基于 React 的框架的通病。

另外,我其实也有过 Vue 的开发经历——本科毕设中曾经包括一个用 Vue 实现的前端,但是毕设项目的甲方中途自己做了前端,我自己的前端代码就烂尾了。基于 Vue 也有 Nuxt.js、VitePress 等静态框架可以选用。

  1. 这实际上把 Astro 最大的优势——内容驱动、提供原生 Markdown 支持——废掉了……

  2. 我尝试了阅读 Astro MDX 集成的源代码,但并没有搞清楚是如何编译的。欢迎在评论区留言提供帮助!

使用 Astro 重建博客