基于 Astro 与 Directus 的新时代 JAMStack 博客实践

最近投入了大量摸鱼时间重构博客。现在大概告一段落了,向大家介绍一下整体的技术选型和具体实现的简要思路。

TLDR:JAMStack 实践,使用最新最潮的前端元框架 Astro,魔改 Astro Paper 主题,搭配 Headless CMS Directus,直通对接思源笔记内容同步,自建 SeaweedFS 分布式文件系统暴露 S3 API 作为图床后端,使用 WebP Server 作为图床前端反代,部署 Cloudflare Pages,实现高度客制化的博客体验。

你可以从这里开始访问我的博客:https://clouder0.com/zh-cn/posts/my-new-blog

Introduction

一路走来,我的博客架构换了一茬又一茬,虽然内容好像更新地也不是很勤快……不,其实还是有持续输出内容的吧!

但是,我现在越来越忙了,也可能是越来越懒了,手动更新博客显然不是什么好的方案,毕竟我目前的内容创作主要集中于:

  • 在思源笔记中写一点为自己记录的内容。顺便,写作是思考的媒介,我也很习惯边写东西边做研究。
  • 写完之后,可能随手复制粘贴发到知乎上。

复制粘贴发送到知乎上,和复制粘贴发送到博客上有什么区别呢,好像也没什么区别,但如果想要支援多平台的话,感觉就很麻烦了。而且我确实很讨厌把差不多的事情重复做很多次。

所以还是来点纯粹的自动化吧!

History

那么讲一讲我博客的历史……曾经用过的方案包括:

  • WordPress,感觉已经是上个时代的产物了。庞大、臃肿,功能过于丰富,破事很多,成本也不低。而且商业化气息浓厚,各种付费主题插件之类的,总之对个人博客场景感觉不是很好的选择。

    • BTW,我对 PHP 没什么好感(
  • typecho,相比 WordPress 当然是清爽简洁了许多,但是……动态博客对个人场景来说还是比较 overhead,因为主体静态内容明明是可以随便 CDN 分发的才对。而且感觉功能又有点过于简陋了。

    • 同样也是上个时代的产物了。唉,PHP.
  • Hexo,静态博客是一阵风啊,突如其来地席卷了大江南北。当然,Hexo 是好的,但当初还是遇到了一些问题,主要是:构建时间长。

    • 对于现在的我来说,不使用 Hexo 的主要原因可能是……定制化没那么方便?基于模板的方案,不是很自由。
    • 主要就是不是很自由吧。
  • Hugo,构建速度非常的神清气爽啊,上个博客就是这个版本的。

    • 主要问题还是定制不太方便,用的是 Golang 的模板语法,原来尝试改过一点主题,真是令人痛苦万分啊。

Arch

那么,归纳一下我的需求:

  • 以静态为主,但要能够结合一些动态内容。最好能直接渲染成 HTML,但支持在里面内嵌一些 Component,走前后端分离架构那套玩法来添加一点动态内容。
  • 高度可定制化,需要什么小功能就能自己搓。

当初我听说过有一个框架叫做 Gatsby,不过现在更流行的是 Astro. Astro 虽然可以用来做博客,不过更多时候会拿来做一些企业的 Landing Page. 它的设计架构非常的漂亮,整体上说:

  • 同时支持 Server Component 和 Client Component,可以混合动静态内容。
  • 使用类似 JSX 的语法,写起来令人倍感亲切,比学奇奇怪怪的模板方便多了。
  • 在 Static Site Generation 阶段,可以直接执行 JavaScript 代码,调用给出的 API 即可,非常非常灵活啊。

是的,所以理论上在 Astro 里面我们可以做到这样的事情:

  • 在 build 阶段,调用 API 拉取 CMS 的内容。根据 API 调用返回值直接构建静态网站。

而如果你不想静态构建的话,Server Side Rendering 也是改个配置的事情。非常好文明。

Implementation

在实际的搭建过程中,顺序大概是这样的:

  • 先用 Astro 把博客的前端跑起来。
  • 搭建 Directus,折腾 Astro 成功对接 Directus 的后端内容。
  • 手搓一个小工具,实现思源笔记导出内容至 Directus 中。
  • 手搓博客的 i18n、评论区、RSS、OgImage 等功能。
  • 跑 SeaweedFS,跑 WebP Server 把图床跑起来。
  • 使用思源笔记的发布插件对接 S3 Backend 方便上传图片。

Astro

虽然 Astro 挺好的, 主要是它可以非常方便地结合前端技术栈,爱用啥用啥。但还是有一些 Drawbacks,目前我注意到的几点不足是:

  • 拉取 Remote Content 的 Content Layer API 还不是很成熟,许多文档缺失。虽然已经可以用了,但官方没有暴露方便的 Markdown Render 接口,导致拉取了远端的 Markdown 内容还要自己想办法渲染。

    • 渲染 Remote Markdown 最后的解决方案是我在官方 Discord Channel 的聊天记录里找到的…
  • Incremental Build 虽然有,但只支持本地 Markdown 文件,只要打开增量构建,Remote Content 会直接消失。果然是非常 Experimental 的 Feature 啊。

  • 默认没有异步构建,或者说默认的 Concurrent Number 为 1,导致如果你的 Single Page Build 过程中有阻塞工作,总构建时长就会非常令人感叹。

  • 开放的 API 感觉缺了不少东西,比如说缺少为使用者提供的 Cache API 之类的,只能自己脏脏地乱写。

  • 没有足够开箱即用的 i18n 支持,虽然有 i18n router,但是灵活度差强人意,而且文档中给的方案也并不好。

Whatever,里面的大部分问题都被我想办法解决掉了。

i18n

具体探索掠过不谈,直接讲目前的 Solution:

使用 paraglidejs,一个 i18n 库,在 yml 中写翻译,然后 compile 成 js 文件,就可以直接在 Astro 中调用了。

但你依然需要为不同的语言生成不同的 static files,要做到这一点的话,可以添加一层路由:
在这里插入图片描述

然后在 export static path 的时候:

import {
    availableLanguageTags, languageTag } from "paraglide/runtime";

import * as m from "paraglide/messages.js";
const posts = [
  ...(await getCollection("blog")),
  ...(await getCollection("directus_blog")),
];

const tags = getUniqueTags(posts.filter(p => p.data.lang === languageTag()));

export function getStaticPaths() {
   
  return availableLanguageTags.map(lang => ({
    params: {
    lang } }));
}

大概是这么个意思,为不同的 language 生成不同的路由。

需要注意:Astro 默认的 Content 必须有 unique slug,而这意味着 [lang]/[slug]​ 这样的路由会导致 zh-cn/mypost​ 和 en/mypost​ 只能指向同一个 mypost.md​,这个时候一个解决方案是:写文件的时候把 slug 开头加上 [lang]-​,然后生成路由的时候删掉。

for (const lang of ["zh-cn", "en"]) {
   
   if
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值