其实我的博客在去年某个时间之后就很少更新了, 哎哎, 也是变得懒狗了(虽然确实是事情一大堆), 当我想再写点啥的时候, 上去一看, 发现加载也是卡的飞起, 有些文章也很臃肿, 我就想着趁暑假来翻新一下.

本来是打算7月就搓好上线的, 但是却好运的找着了实习, 然后就开始猛猛忙实习, 只能抽点摸鱼时间(周末当然是要休息嘟)来折腾这个.总而言之, 现在终于是把整个搓的差不多了. 稍微写点东西记一记吧

转到 Astro

我本来是用 hexo 的, 这也是个很成熟并且社区, 主题也很丰富的框架. 但是我一直没找着简约又合我口味, 还有高性能的主题. 我一直用的是 particlex, 这个主题写的样式很合我胃口, 但是加载很慢, 并且扩展页面有些困难(没有文档), 我之前搓过一个 rss, 一个单独的友链页面, 以及一个 toc, 但是加载问题一直没想到什么很好的解决办法

干脆直接换个框架吧! 找一个能从比较底层开始写, 同时支持 markdown 渲染, 又可以有轻量启动方案的框架. 然后我就找到了 Astro.

Astro 其实并不是为博客服务的框架, 他的功能非常强大, 可以作为各种商城, Web应用的框架. 但是他有几个(宣传的)特点很符合我的需求:

  • 默认零 js; 没了 js, 一个站点的加载, 渲染速度都会巨幅增加. Astro 有个 frontmatter 脚本, 这些脚本的功能基本可以满足我搓一个博客的需求了(比如统一渲染模板, 统计, 动态路由等等), 并且他们都会在构建的时候运行, 然后生成的产物中是不会有 js 的, 这样将渲染消耗全部集中在渲染前, 就能让分发的页面访问起来很丝滑流畅了
  • 内容集合机制很方便; 内容集合可以规定一个博客规范, 把 markdown 的元数据用在 layout, 统计, 分页等等组件上. 这个机制在 hexo 似乎也有类似实现, 但是 Astro 的内容集合机制是可以用 ts 来严格类型的, 并且元数据可以完全自定义, 用在哪里也很自由. 相对来说更时候从框架开始博客.
  • 动态路由支持; hexo 也有动态路由, 他是自动生成的, 也是可以自定义的. 但是自动生成就会容易产生混乱, 推断整体架构需要考虑下他会怎么生成(尽管他似乎很直接了当). Astro 的动态路由要自己写, 并且参数, 范围等等都是高度自定义的. 我觉得这种掌握架构的感觉能让我更好的搓整个架构

这些东西足够有吸引力了, 然后就开始搓!

组织博客

文章

首先我们要先知道我们要怎么搓整体的架构. 为了之后可以更加专注于内容, 我们需要自动化的生成路由, 只需要往指定目录放一个 markdown, 就可以产生其路由. 这一点可以用动态路由机制做到

接着就是给页面统一的渲染. Astro 会自动将 markdown 渲染成 html, 统一组织页面的布局, 则有 layout 可以满足. 然后加一个全局的 css 就解决了样式问题.

所以这条链子就是, 用内容集合给 markdown 加上元数据, 接着用 props 全局对象把这些元数据传到 layout 组织起来, 加个整体样式, 最后用动态路由组织起来. 非常直观.

页面

页面组织比博客简单的多, 一个 Astro 文件就是一个组件, 组件可以直接渲染成 html, 同时也可以灵活组织(比如复用一下资料卡片直接用在每个页面之类的). 这里就不多讲了.

图片

我本来的博客加载慢还有个很重要的原因就是图片加载慢. 当时有尝试过做 lazyload 之类的机制, 但是效果不太好. 后来干脆没管了.

Astro 还默认支持图片懒加载, 真是很舒服, 但是 markdown 的图片懒加载需要给 img 标签加个属性(默认不懒加载), 用 rehypePlugins 插件(用来自定义修改渲染后的 HTML AST 的插件)可以很方便的实现:

function rehypeLazyImages() {
  return function transform(tree) {
    function visit(node) {
      if (!node || typeof node !== 'object') {
        return;
      }

      if (node.type === 'element' && node.tagName === 'img') {
        node.properties ??= {};
        node.properties.loading ??= 'lazy';
        node.properties.decoding ??= 'async';
      }

      if (Array.isArray(node.children)) {
        node.children.forEach(visit);
      }
    }

    visit(tree);
  };
}

遍历 AST 树, 找到 img 元素, 给他加上懒加载的标签就行

然后放到 markdown 渲染的插件里:

export default defineConfig({
  markdown: {
    processor: unified({
      remarkPlugins: [remarkMath],
      rehypePlugins: [rehypeKatex, rehypeLazyImages],
    }),
  },
});

这里因为依赖了 KateX 来渲染公式, 他是用传统处理器(Unified)写的, 所以就顺从它了.

同时把图片都转换成 webp, 体积小了好多. 这个问题也就迎刃而解了.

样式

样式采用了 tailwind + 整体 css 的方案. 页面, layout, 组件的样式都是 tailwind 写的, 因为这些不适合用通用样式. 而 markdown 是自动渲染成 HTML 的, 这个方案行不通也很麻烦, 所以为他做一个整体的 css 就行.

至于样式具体怎么搓, 交给 GPT 大人烦恼吧, 我只需要提供描述和微调就好了 (ΦωΦ).

部署

由于构建产物全是 html 和 css, 服务器只需要分发静态文件就行了. 只需要本地构建好, 用 rsync 和服务器同步就行, 服务器上则部署一个 nginx 来分发即可.

不得不说 rsync 真好用, 增量传输和同步删除真的很好用👍.

小结

最后把七七八八的组件, 分页, 跳转逻辑之类的处理好之后, 就是现在的样子了. 其实还有一些想法暂且没实现, 而且有些机制其实不那么人性化, 之后估计还会更新一下. 不过总体来说, 算是成功的搓了一个看起来还不错, 加载也非常迅捷快速的博客了(我就感觉到快~). 唔姆唔姆, 没白干.