Hello Astro

2024-04-01

再一次换博客了。

翻了翻记录,从 Farbox -> Bitcron -> Hugo 一路到 Astro,已经第四次了,可能花在折腾博客的时间要远远多于记录的时间,实在惭愧;特别是上一次花许多精力迁到 Hugo 之后不仅主题没修改完成,笔记也没写几篇就荒废了,虽然很大一部分是生活和工作的变动所致。

缘由

作为一个主力使用 Python 的后端程序员,自然也写过几行 Flask 代码,但总是束手束脚的,写着很别扭;但随着时间的推移越发觉得需要有个供内部使用的的 Web 管理界面(用于大疆机场和海康球机管理)。而恰好上个项目第一阶段完成后,也有这个需求提出,便想着自己实现试试,刚好也可以借机学习,于是准备先找个简单的熟悉下。

不过最开始并没有考虑拿博客练手,毕竟两者形态不是一个类型,而是先选了一个展示优衣库产品的页面,采用 Vue + Vite + Typescript + daisyUI 实现。只是写着写着便想到这和我以前想在博客加一个模块展示书籍的功能何其相似,一旦有这个想法之后就挥之不去,加上时不时刷到许多迁移博客到 Astro 的推,于是在上周采取了行动。

选择 Astro 的原因

除了前面提到的原因之外,Astro 也恰好是 TypeScript 实现,且支持 Vue 框架,官方网站上还有非常完善的教程,跟着走一遍就能自己实现一个有基本功能的博客主题,简直完美。

从 Hugo 到 Astro

URL 路径

在 Hugo 中,文章 URL 路径是通过 frontmatter 读取的,在 Astro 默认是文件名作为路径,因此在 Astro 需要先判断 frontmatter 是否存在 URL 字段:

1
slug: entry.data.url || entry.slug

另外原本的文章 URL 路径形式是:immwind.com/hello-astro,如果按照 Astro 教程写完,路径是:immwind.com/posts/ello-astro,会导致就链接对不上,不过这个只要把 […slug].astro 文件移到 src/pages 目录下即可。

发表时间

在 Astro 中,默认发布时间字段是 pubDate,而之前都是 Date(包括在 Obsidian 也是一样)不过这个改动倒是比较简单;被坑的地方是在 rss.xml.js 中这个字段名称必须是 pubDate

除了字段之外,时间格式是另一个比较麻烦的点,目前还没有完美解决。现存的文章都是使用 2024-04-01 10:24 格式,但在 Astro 中用的是 UTC 格式:2021-07-08T12:00:00-04:00,这个如果想通过 Zod 定义只能折中使用 yyyy-mm-dd 格式,不包含时间。

时间格式化代码是参考官方示例改的,调用后生成的时间格式是 yyyy-mm-dd

1
---
2
interface Props {
3
date: Date;
4
}
5
6
const { date } = Astro.props;
7
---
8
9
<time datetime={date.toISOString()}>
10
{
11
date.toLocaleDateString("zh-CN", {
12
year: "numeric",
13
month: "2-digit",
14
day: "2-digit",
15
}).replace(/\//g, "-")
16
}
17
</time>

封面图

之前在 Hugo 使用的主题需要提供一张作为封面图,当时做了兼容仅把特定目录的笔记在首页展示,其他笔记只在 /Archives 查看。虽然现在 AI 生成图片很方便,但是每篇笔记都提供一张图片还是蛮麻烦的,所以目前仅在文章内展示图片,这样封面图就非必须了。

不过另一个想法是等主要界面确定后,如果文章内有图片,则自动提取第一张图(这个功能已经实现),然后做成首页展示时对应笔记的背景(模糊处理)。

文章摘要

以前的博客好像都是支持使用 <!--more--> 做为文章摘要,但在 Astro 中居然没找到对应的实现方式。后来按照自己的习惯写了一个比用 <!--more--> 更好的方案:提取第一个标题之前的文字,截取指定字数作为摘要放到 frontmatter 中。

添加 Vercel 依赖后报错

如果博客是按官方教程编写的,添加 Vercel 依赖准备部署后,本地只要访问 getStaticPaths 的页面会出现报错:astro vercel Cannot read properties of undefined (reading ‘render’),如果是像我一样部署到 Vercel 后访问文章出现提示:vercel http error 500 也是同样的问题。有两种解决方式:

第一种是在 astro.config.mjs 中直接修改为静态(SSG)模式:

1
import vercel from "@astrojs/vercel/static";
2
export default defineConfig({
3
output: "static",
4
adapter: vercel(),
5
});

另一种是修改所有 getStaticPaths 引用到的页面,比如修改 […slug]:

1
---
2
import type { CollectionEntry } from 'astro:content'
3
import { getCollection } from 'astro:content';
4
import PostLayout from "@layouts/PostLayout.astro";
5
6
const posts = await getCollection("posts")
7
type Props = CollectionEntry<"posts">
8
9
const { slug } = Astro.params;
10
const post = posts.find((page) => page.slug === slug);
11
if (!post) return Astro.redirect("/404");
12
const { Content } = await post.render();
13
---
14
15
<PostLayout frontmatter={post.data}>
16
<Content />
17
</PostLayout>

问题的完整解决方式可参考:@astro/vercel causes error with render function · Issue #6012 · withastro/astro

Feature

希望实现的功能:

  • 摘要提取(正文第一段)
  • 从正文提取第一张图片为封面图
  • 阅读时间和字数统计
  • RSS
  • OGP
  • TOC
  • 搜索
  • sitemap
  • Markdown 主题可选
  • 读书页面(关联豆瓣?)
  • 在用软件、设备
  • Friends

技术栈

  • TypeScript
  • Astro
  • Tailwind CSS 准备换成 UnoCSS
  • daisyUI

最后

最开始的想法是按照官方教程走一遍,只要实现 Markdown 笔记渲染,并且 URL 路径和 Hugo 保持一致就可以部署了。但在编写的过程中不断有想法冒出(主要是关于主题设计),导致改了又改;为了不重蹈覆辙,索性都先不管,先部署,后面边写(笔记)边改。


Nothing in the world could bother me,
Cause I was living in a world of ecstasy,
But now you’re gone,
I’m just a daydreamer,walking in the rain,
Chasing after rainbows I may never find again.