banner
L,.G

L,.G

读书|新知|生活禅

你好 AstroPaper

我可以说自己也是颜狗吗?一个独立博客的界面是否好看,多多少少都显示出博主本人的审美和品味。

举例来说,陈皓先生的酷壳,可以看到中年男人特有的内敛与落落大方的举止;椒盐豆豉Owen 的博客都有一种粗糙中的精细,蕴藏着质朴的知性美;小胡同学的印记,可以看出年轻人的不羁与传统家教的底色在相互碰撞;大宇的 Another Dayu,如同他的摄影作品一样透露着沉着冷静;阿杰的 Jack’s Space 则是一个可爱大男孩儿的既视感…… 一个人的博客观感,往往在不经意间透露给读者许多轻描淡写的隐喻。

我曾想在原博客框架中复刻 AstroPaper 主题,奈何能力有限,就干脆认真贯彻拿来主义的精神,将博客框架从 Hugo 迁移到了 Astro。

「人类发展的历程往往是波浪式前进与螺旋式上升。」在应用这个主题时,充分印证了这个哲学命题。

Table of contents#

时区的纠结#

使用 Astro 搭建的博客,大多数采用了 ISO 8601 的时间书写格式用于显示文章的发布或修改日期。以我使用的 AstroPaper 主题为例,文章的 frontmatter 中 pubDatatime 的写法是 2024-03-27T12:00:00+08:00,表示的是国际标准时间(UTC)中的一个具体时刻,即 2024 年 3 月 27 日上午 12 点整。“+08:00” 代表的是东八区,即北京时间。

主题的 src/components/Datetime.tsx 源代码,会根据访问者所在地显示文章的时区。这就导致了如果从中国大陆用美西的代理访问我的博客,显示的文章发布时间为美西时间。而访问者本身并不是在美国,看到的发布时间就会奇怪。

所以,我干脆修改了这段代码,使其强制显示为北京时间。对此,我在 About 中也做了说明。

Because my blog doesn't feature English content yet, I've adjusted the theme's source code to set the time zone to GMT+8. This ensures that all the timestamps on the articles correspond to Beijing time.

修改后的代码如下:

import { LOCALE } from "@config";

interface DatetimesProps {
  pubDatetime: string | Date;
  modDatetime: string | Date | undefined | null;
}

interface Props extends DatetimesProps {
  size?: "sm" | "lg";
  className?: string;
}

export default function Datetime({
  pubDatetime,
  modDatetime,
  size = "sm",
  className,
}: Props) {
  return (
    <div className={`flex items-center space-x-2 opacity-80 ${className}`}>
      <svg
        xmlns="http://www.w3.org/2000/svg"
        className={`${
          size === "sm" ? "scale-90" : "scale-100"
        } inline-block h-6 w-6 min-w-[1.375rem] fill-skin-base`}
        aria-hidden="true"
      >
        <path d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"></path>
        <path d="M5 22h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2h-2V2h-2v2H9V2H7v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2zM19 8l.001 12H5V8h14z"></path>
      </svg>
      {modDatetime && modDatetime > pubDatetime ? (
        <span className={`italic ${size === "sm" ? "text-sm" : "text-base"}`}>
          Updated:
        </span>
      ) : (
        <span className="sr-only">Published:</span>
      )}
      <span className={`italic ${size === "sm" ? "text-sm" : "text-base"}`}>
        <FormattedDatetime
          pubDatetime={pubDatetime}
          modDatetime={modDatetime}
        />
      </span>
    </div>
  );
}

const FormattedDatetime = ({ pubDatetime, modDatetime }: DatetimesProps) => {
  const myDatetime = new Date(
    modDatetime && modDatetime > pubDatetime ? modDatetime : pubDatetime
  );

  const bjDatetime = new Date(
    myDatetime.toLocaleString("en-US", { timeZone: "Asia/Shanghai" })
  );

  const date = bjDatetime.toLocaleDateString(LOCALE.langTag, {
    year: "numeric",
    month: "short",
    day: "numeric",
  });

  const time = bjDatetime.toLocaleTimeString(LOCALE.langTag, {
    hour: "2-digit",
    minute: "2-digit",
  });

  return (
    <>
      <time dateTime={bjDatetime.toISOString()}>{date}</time>
      <span aria-hidden="true"> | </span>
      <span className="sr-only">&nbsp;at&nbsp;</span>
      <span className="text-nowrap">{time}</span>
    </>
  );
};

Open Graph#

在主题的教程文档 Dynamic OG image generation in AstroPaper blog posts 中,主题的创作者说 Satori 在自动生成文章的动态 OG Image 时,如果文章标题不是英文的,显示效果可能不好。

这哪里只是不好,简直…… 一…… 言…… 难…… 尽,好不好!!!

image

虽然,OG Image 说到底只是在社交媒体进行分享时好看一些罢了,有没有并不关键。但对于主要以中文创作的我来说,这种 OG Image 还是无法忍受的。

我试图修改代码,在 generateOgImages.tsx 中尝试使用 Sarasa Gothic SC 和 Source Han Sans,但是在部署时都出现报错。询问 AI 后,给出的解决办法是将字体下载到 public/font 文件夹中,从本地加载。想起之前折腾 Nobelium 时也是这样操作,会让网页加载变慢,并且消耗很多资源。遂另寻他路。

「只要思想不滑坡,方法总比困难多。」自动生成的 OG Image 无非就是不需要花费时间去为文章制作头图。想必很多博主都有这样的经历,花费不了多少时间就写好了文章,结果制作头图花费的时间比写作还多。想起来就觉得可怕。

想到自己的图库里有几张之前使用 Typecho 时的图片,那我为什么不将它们当成 OG Image 轮流用呢?无非是在文章的 frontmatter 中增加一个 ogImage 的属性而已。frontmatter 也可以通过 Raycast Snippet 设置,并不需要我去记忆。

image

是不是赏心悦目了起来。

评论组件#

这次原本想要使用 Remark42 作为评论组件,奈何我查了很多资料,问了 AI,也还是没有搞清楚到底怎么操作。只能继续沿用我在 Hugo 中使用的 Twikoo。

要在 Astro 中使用 Twikoo 作为评论组件,步骤相对简单,却是我踩坑最多的。具体踩坑的过程就不详述了,只给出方法。

🙏 感谢 1900 提供的帮助。

此段原内容已删除,仅保留新内容。

1900 哥哥给我看了一篇文章,里面提到了通过创建新的组件模板引入评论组件的方式。

首先,在 src/components 中新建 Comments.astro 文件,作为组件模板。

然后添加如下代码:

<div id="tcomment"></div>

<script>
document.addEventListener('astro:page-load', () => {
function loadTwikoo() {
const commentsContainer = document.getElementById('tcomment');
if (commentsContainer) {
const script = document.createElement('script');
script.src = 'https://cdn.staticfile.org/twikoo/1.6.32/twikoo.all.min.js';
script.async = true;
script.onload = () => {
  const initScript = document.createElement('script');
  initScript.innerHTML = `
    twikoo.init({
        envId: '您的环境 ID',
        el: '#tcomment',
    });
`;
  document.body.appendChild(initScript);
};
document.body.appendChild(script);
}
}
loadTwikoo();
});
</script>

接着,到需要引入评论组件的模板中插入 <Comments /> 组件,例如我在 PostDetails.astroAboutLayout.astro 中做了修改。

---
<!--引入的其他组件-->
import Comments from "@components/Comments.astro";
---
<!--其他代码-->
  <Comments /> //这里引入 Comments 组件,放置在 </main> 标签之前
  </main>
  <Footer />
</Layout>

但是,此时会发现评论组件的显示明显不美观,占据了整个页面的宽度。

image

所以,此时需要在 Comment.astro 中新增一段 <style> 去规定显示的格式。那么完整的评论模板的代码为:

<div id="tcomment"></div>

<style is:global>
  main,
  #comment {
    @apply mx-auto w-full max-w-3xl px-4 pb-12;
  }
</style>

<script>
document.addEventListener('astro:page-load', () => {
function loadTwikoo() {
const commentsContainer = document.getElementById('tcomment');
if (commentsContainer) {
const script = document.createElement('script');
script.src = 'https://cdn.staticfile.org/twikoo/1.6.32/twikoo.all.min.js';
script.async = true;
script.onload = () => {
  const initScript = document.createElement('script');
  initScript.innerHTML = `
    twikoo.init({
        envId: '您的环境 ID',
        el: '#tcomment',
    });
`;
  document.body.appendChild(initScript);
};
document.body.appendChild(script);
}
}
loadTwikoo();
});
</script>

这样,评论组件就设置好,并且可以跟随页面一起启用了。

RSS 全文输出#

AstroPaper 主题中,RSS 并不输出全文。对于使用 RSS 订阅了博客的读者而言并不友好。所以,我还更改了 src/pages/rss.xml.ts,并且只获取最新的十篇文章。

这里需要注意的是,我引入了一个新的依赖 marked,需要先在项目文件中通过 npm install markedyarn add marked 安装这个库。

import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
import getSortedPosts from "@utils/getSortedPosts";
import { SITE } from "@config";

// 引入 marked 依赖,通过 npm install marked 安装
import { marked } from "marked";

export async function GET() {
  const posts = await getCollection("blog");
  const sortedPosts = getSortedPosts(posts);

  // 只获取最新的10篇文章
  const latestPosts = sortedPosts.slice(0, 10);

  return rss({
    title: SITE.title,
    description: SITE.desc,
    site: SITE.website,
    items: latestPosts.map(({ data, slug, body }) => {
      // 移除 "## Table of Contents" 部分
      const cleanedBody = body.replace(
        /## Table of Contents\s*([\s\S]*?)(?=\n## |\n# |$)/g,
        ""
      );
      return {
        link: `posts/${slug}/`,
        title: data.title,
        description: data.description,
        pubDate: new Date(data.modDatetime ?? data.pubDatetime),
        content: marked(cleanedBody),
      };
    }),
  });
}

其他的一些调整#

余下的一些调整就是很常规的操作。例如,在 src/styles/base.css 中修改主题配色;在 src/components/Header.astro 中,为博客添加 Umami 代码,修改导航栏的内容;在 src/components/Footer.astro 中,增加 Copyright 声明等。

此外,还编辑了一个新的页面模板,用于博客的一些独立页面。

整体而言,此次迁移博客的框架还算顺利,中间虽然有些曲折,好在大多数都解决了。余下的也并不影响使用,留待日后慢慢解决吧。

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.