chore: update the old blog article, enrich the content with more images.

This commit is contained in:
Yufan Sheng 2024-10-16 01:04:38 +08:00
parent 78297bb249
commit c1b3f54161
Signed by: syhily
GPG Key ID: 9D18A22A7DCD5A9B
14 changed files with 60 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -1,7 +1,6 @@
--- ---
import { loadComments } from '@/components/comment/artalk'; import { increaseViews, loadComments } from '@/components/comment/artalk';
import Comment from '@/components/comment/Comment.astro'; import Comment from '@/components/comment/Comment.astro';
import { increaseViews } from '@/helpers/db/query';
import { urlJoin } from '@/helpers/tools'; import { urlJoin } from '@/helpers/tools';
import options from '@/options'; import options from '@/options';
@ -15,7 +14,7 @@ const { commentKey, title } = Astro.props;
const comments = await loadComments(commentKey, title, 0); const comments = await loadComments(commentKey, title, 0);
// Increase the PV. // Increase the PV.
await increaseViews(commentKey); await increaseViews(commentKey, title);
--- ---
<div id="comments" class="comments py-5"> <div id="comments" class="comments py-5">

View File

@ -33,6 +33,20 @@ export const loadComments = async (key: string, title: string | null, offset: nu
return data != null ? (data as Comments) : data; return data != null ? (data as Comments) : data;
}; };
export const increaseViews = async (key: string, title: string) => {
await fetch(urlJoin(server, '/api/v2/pages/pv'), {
method: 'POST',
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
body: JSON.stringify({
page_key: key,
page_title: title,
site_name: options.title,
}),
});
};
export const createComment = async (req: CommentReq): Promise<ErrorResp | CommentResp> => { export const createComment = async (req: CommentReq): Promise<ErrorResp | CommentResp> => {
const user = await queryUser(req.email); const user = await queryUser(req.email);
if (user !== null && user.name !== null) { if (user !== null && user.name !== null) {

View File

@ -2,7 +2,7 @@
title: 弃用 WordPress 了,但我相当“后悔” title: 弃用 WordPress 了,但我相当“后悔”
slug: switch-blog-to-nextjs slug: switch-blog-to-nextjs
date: 2024-04-07 16:09:09 date: 2024-04-07 16:09:09
updated: 2024-06-23 15:21:05 updated: 2024-10-16 00:25:05
tags: tags:
- 博客 - 博客
category: 编程 category: 编程
@ -48,18 +48,26 @@ Logo 部分的字体,主要是基于 [M+A1](https://booth.pm/ja/items/2347968)
## 技术选型 ## 技术选型
![Next.js](/images/recaps/switch-blog-to-nextjs/next.js.png)
技术选型上这次有点激进,相比已经成熟很好用的 VitePress 等动静结合的生成工具,我义无反顾地选用了 Next.js 的 App Router运行环境选用了更快的 Bun。至于其他方面基本是照着 [leerob](https://leerob.io) 的博客进行仿写。所以文章格式不再是常见的 Markdown而是能插入动态内容的 MDX。对于历史的评论我还是舍不得丢弃毕竟有 3000 多条。所以再三对比之下,我选择使用 [Artalk](https://artalk.js.org) 进行存储。有了多说的经历后,我也不敢使用任何第三方的 SaaS 服务存储评论,毕竟数据在自己手里才最安全。 技术选型上这次有点激进,相比已经成熟很好用的 VitePress 等动静结合的生成工具,我义无反顾地选用了 Next.js 的 App Router运行环境选用了更快的 Bun。至于其他方面基本是照着 [leerob](https://leerob.io) 的博客进行仿写。所以文章格式不再是常见的 Markdown而是能插入动态内容的 MDX。对于历史的评论我还是舍不得丢弃毕竟有 3000 多条。所以再三对比之下,我选择使用 [Artalk](https://artalk.js.org) 进行存储。有了多说的经历后,我也不敢使用任何第三方的 SaaS 服务存储评论,毕竟数据在自己手里才最安全。
博客以前最喜欢的音乐播放器是 Hermit-X对应的作者荒野无灯已经神隐很久获取网易音乐信息的 [Meting API](https://github.com/metowolf/Meting) 也没持续更新。而 APlayer 更是不知道 [DIYgod](https://diygod.cc) 什么时候能来“扫墓”,上次 APlayer 的诈尸还只是更新了个 License。所以在新博客环境只有 [Aplayer React](https://aplayer-react.js.org) 这个名为 APlayer 实则是借着其样式用 React 完全重写的播放器可用,其在 RSC 下有点小问题,但估计短时间内作者也没时间修复。(谁叫我是前端菜鸟呢) 博客以前最喜欢的音乐播放器是 Hermit-X对应的作者荒野无灯已经神隐很久获取网易音乐信息的 [Meting API](https://github.com/metowolf/Meting) 也没持续更新。而 APlayer 更是不知道 [DIYgod](https://diygod.cc) 什么时候能来“扫墓”,上次 APlayer 的诈尸还只是更新了个 License。所以在新博客环境只有 [Aplayer React](https://aplayer-react.js.org) 这个名为 APlayer 实则是借着其样式用 React 完全重写的播放器可用,其在 RSC 下有点小问题,但估计短时间内作者也没时间修复。(谁叫我是前端菜鸟呢)
![ContentLayer](/images/recaps/switch-blog-to-nextjs/contentlayer.png)
MDX 一开始是手动使用 `fs` 加载,使用 `grey-matter` 解析 `matter` 后再用 `next-mdx-remote` 渲染。但是鉴于自己太菜,很多页面的渲染都需要全量读取博文解析。后面改成了[推友推荐](https://twitter.com/ilovek8s/status/1776809454790676827)的 `contentlayer` 先渲染静态化,再进行加载。但瞅着 `contentlayer` 这半死不活的状态,未来大概率还是要弃。😭 MDX 一开始是手动使用 `fs` 加载,使用 `grey-matter` 解析 `matter` 后再用 `next-mdx-remote` 渲染。但是鉴于自己太菜,很多页面的渲染都需要全量读取博文解析。后面改成了[推友推荐](https://twitter.com/ilovek8s/status/1776809454790676827)的 `contentlayer` 先渲染静态化,再进行加载。但瞅着 `contentlayer` 这半死不活的状态,未来大概率还是要弃。😭
## Update 2024/04/14 ## Update 2024/04/14
![ContentLayer](/images/recaps/switch-blog-to-nextjs/velite.jpg)
没错,一周后我又来屁颠屁颠地更新博客啦,一周过去后,我的博客也发生了不少变化。首先是搜索终于支持了,使用的是 fuse.js。前文提到的 Contentlayer 也被我废弃,换成了更好用的 Velite。 没错,一周后我又来屁颠屁颠地更新博客啦,一周过去后,我的博客也发生了不少变化。首先是搜索终于支持了,使用的是 fuse.js。前文提到的 Contentlayer 也被我废弃,换成了更好用的 Velite。
## Update 2024/05/22 ## Update 2024/05/22
![ContentLayer](/images/recaps/switch-blog-to-nextjs/astro.jpg)
今天,我把博客从 Next.js 迁移到了 Astro重写了部分代码的实现优化了部分设计。Next.js 版本的博客,内容管理和生成工具使用的是 Velite.js它的设计很有想法作者也很有经验。但是对应的 Next.js 与其集成后,却给我带来了很多解决不了的问题。首当其中的就是 Next.js 默认是 SSR我在 MDX 中生成的内容,部分需要 inline 到 Client 端使用 JS 去动态执行,结果无法实现。其次就是 Astro 先天的在内容管理与编写上下了不少功夫。基于上述理由,我花了两天多一点的时间,就轻松把博客用 Astro 重写了。 今天,我把博客从 Next.js 迁移到了 Astro重写了部分代码的实现优化了部分设计。Next.js 版本的博客,内容管理和生成工具使用的是 Velite.js它的设计很有想法作者也很有经验。但是对应的 Next.js 与其集成后,却给我带来了很多解决不了的问题。首当其中的就是 Next.js 默认是 SSR我在 MDX 中生成的内容,部分需要 inline 到 Client 端使用 JS 去动态执行,结果无法实现。其次就是 Astro 先天的在内容管理与编写上下了不少功夫。基于上述理由,我花了两天多一点的时间,就轻松把博客用 Astro 重写了。
重写之后发现 Astro 其实还是比较不成熟,很多东西文档并不清楚,还是要看源码来理清其设计想法。比如 `Astro:content` 的设计原理和其使用的细节问题,再比如 `Astro:asset` 这个特定的 import.meta 的使用问题。整体上而言,很多细节问题其实是 Astro 变化太快对应的文档还没有来得及讲清楚。但是最让我失望的是Astro 的 SSR 并不完善,它的 inline CSS 和 JS 的实现,其实是靠 Vite 整合 Rollup 提供的能力。所以在 SSR 上,如果你在一个 Astro Component 里面 `is:inline` 去定义 JS很有可能会遇到因为模块重用而导致的坑。 重写之后发现 Astro 其实还是比较不成熟,很多东西文档并不清楚,还是要看源码来理清其设计想法。比如 `Astro:content` 的设计原理和其使用的细节问题,再比如 `Astro:asset` 这个特定的 import.meta 的使用问题。整体上而言,很多细节问题其实是 Astro 变化太快对应的文档还没有来得及讲清楚。但是最让我失望的是Astro 的 SSR 并不完善,它的 inline CSS 和 JS 的实现,其实是靠 Vite 整合 Rollup 提供的能力。所以在 SSR 上,如果你在一个 Astro Component 里面 `is:inline` 去定义 JS很有可能会遇到因为模块重用而导致的坑。
@ -82,12 +90,16 @@ Astro Container API 前前后后经过数次修改。在最新的 Astro 4.11 中
### Artalk 前端评论模块弃用 ### Artalk 前端评论模块弃用
![ContentLayer](/images/recaps/switch-blog-to-nextjs/replace-artalk.png)
Artalk 作为后端评论系统,其实没啥问题,并且有相当不错的安全机制和设计,但是其前端样式怎么定义都不是很好看。早在准备自己实现评论系统的时候,第一个想法就是基于 Artalk 的 Rest API 改成 SSR 的模式加载评论。目前经过数次修改,其评论样式和效果已经和以前使用的 WordPress 基本接近。 Artalk 作为后端评论系统,其实没啥问题,并且有相当不错的安全机制和设计,但是其前端样式怎么定义都不是很好看。早在准备自己实现评论系统的时候,第一个想法就是基于 Artalk 的 Rest API 改成 SSR 的模式加载评论。目前经过数次修改,其评论样式和效果已经和以前使用的 WordPress 基本接近。
在做评论系统的替换的同时,也在遗忘了很久的百度网盘的网站备份目录中找到了多说和 Disqus 的备份。经过修改和迁移,已经成功将多说的评论全部导入进 Artalk。而 Disqus 的备份,受限于欧盟的 GDPR 政策,已经不再提供评论者的 Email 信息,所以只能基于历史评论进行检索对比来确定用户信息进行导入。虽然已经尽了全力,但还是有部分评论被废弃。 在做评论系统的替换的同时,也在遗忘了很久的百度网盘的网站备份目录中找到了多说和 Disqus 的备份。经过修改和迁移,已经成功将多说的评论全部导入进 Artalk。而 Disqus 的备份,受限于欧盟的 GDPR 政策,已经不再提供评论者的 Email 信息,所以只能基于历史评论进行检索对比来确定用户信息进行导入。虽然已经尽了全力,但还是有部分评论被废弃。
### Astro Actions 的使用 ### Astro Actions 的使用
![ContentLayer](/images/recaps/switch-blog-to-nextjs/astro-actions.png)
因为博客的评论模块的替换,导入需要引入大量的 Rest 接口。结合以前有的喜欢按钮等接口,博客一共累计超过 4 个接口。以前都是散乱在 pages 目录里面,这次一并使用了 [Astro 4.8](https://astro.build/blog/astro-480/) 的 Actions 进行统一定义和管理,整体感受如下: 因为博客的评论模块的替换,导入需要引入大量的 Rest 接口。结合以前有的喜欢按钮等接口,博客一共累计超过 4 个接口。以前都是散乱在 pages 目录里面,这次一并使用了 [Astro 4.8](https://astro.build/blog/astro-480/) 的 Actions 进行统一定义和管理,整体感受如下:
1. Actions 和 tRPC 的使用体验基本相似,都是 Typed 的。 1. Actions 和 tRPC 的使用体验基本相似,都是 Typed 的。
@ -96,14 +108,45 @@ Artalk 作为后端评论系统,其实没啥问题,并且有相当不错的
### Astro CDN 功能的整合和实现 ### Astro CDN 功能的整合和实现
![ContentLayer](/images/recaps/switch-blog-to-nextjs/astro-uploader.png)
Astro 在 [2.2](https://astro.build/blog/astro-220/) 引入了 CDN Support其主要目的是对动态构建的 CSS、JS 和图片等内容,可以自定义它们的访问路径和 URLAstro 会在最终构建的网站结果中予以替换对应的资源路径。但是 Astro 只实现前面说的内容,如果想要在生产环境能实现全套的 CDN 支持,还需要将构建出来的文件上传到对应的 CDN 服务提供商。如,上传到 UPYUN、七牛云、S3 等对象存储。 Astro 在 [2.2](https://astro.build/blog/astro-220/) 引入了 CDN Support其主要目的是对动态构建的 CSS、JS 和图片等内容,可以自定义它们的访问路径和 URLAstro 会在最终构建的网站结果中予以替换对应的资源路径。但是 Astro 只实现前面说的内容,如果想要在生产环境能实现全套的 CDN 支持,还需要将构建出来的文件上传到对应的 CDN 服务提供商。如,上传到 UPYUN、七牛云、S3 等对象存储。
在这里我选用了用了近 10 年之久的 UPYUN第一版是使用 0.25 引入的 [Astro Integration API](https://astro.build/blog/astro-025/#new-astro-integrations) 来进行实现。Astro Integration API 是一个非常经典的观察者模式的设计,它对应了 Astro 构建的几个不同阶段允许你定义不同阶段的特殊勾子Astro 会在对应阶段调用你的自定义逻辑。对于我而言,我只需要在 `'astro:build:done'` 阶段读取所有想要上传的目录,进行上传即可。 在这里我选用了用了近 10 年之久的 UPYUN第一版是使用 0.25 引入的 [Astro Integration API](https://astro.build/blog/astro-025/#new-astro-integrations) 来进行实现。Astro Integration API 是一个非常经典的观察者模式的设计,它对应了 Astro 构建的几个不同阶段允许你定义不同阶段的特殊勾子Astro 会在对应阶段调用你的自定义逻辑。对于我而言,我只需要在 `'astro:build:done'` 阶段读取所有想要上传的目录,进行上传即可。
这期间对上传逻辑进行了多次重构,第一版使用 UPYUN Node.js SDK但是这个 SDK 年久失修,使用的还是有安全问题的 axios 版本。对于已经习惯上 fetch 一把梭的我,有点生理不适。考虑到很多云存储都支持了 S3 协议,并且使用 S3 协议能更灵活地切换到不同的存储服务。在第二版使用了 Client S3 来进行实现,这里面遇到的唯一问题就是 ContentType 需要显式设置,而原先的 UPYUN Rest API 能根据文件名自动设置。 这期间对上传逻辑进行了多次重构,第一版使用 UPYUN Node.js SDK但是这个 SDK 年久失修,使用的还是有安全问题的 axios 版本。对于已经习惯上 fetch 一把梭的我,有点生理不适。考虑到很多云存储都支持了 S3 协议,并且使用 S3 协议能更灵活地切换到不同的存储服务。在第二版使用了 Client S3 来进行实现,这里面遇到的唯一问题就是 ContentType 需要显式设置,而原先的 UPYUN Rest API 能根据文件名自动设置。
![ContentLayer](/images/recaps/switch-blog-to-nextjs/opendal.png)
第三版使用了基友 Tison 安利的 Apache OpenDAL™ 进行了重构,还是走的 S3 协议。Apache OpenDAL™ 是一个使用 Rust 编写的统一云存储层抽象接口,整体的代码质量和实现都很不错。但是 Node.js 部分的 Binding 使用 napi-rs 实现,对应的文档写得比较抽象,我是看 Rust 部分的文档反向推导 Node.js binding 该如何使用,期待文档的进一步完善。同时因为一些[已知问题](https://github.com/apache/opendal/issues/4782),暂时还无法直接在生产环境使用。 第三版使用了基友 Tison 安利的 Apache OpenDAL™ 进行了重构,还是走的 S3 协议。Apache OpenDAL™ 是一个使用 Rust 编写的统一云存储层抽象接口,整体的代码质量和实现都很不错。但是 Node.js 部分的 Binding 使用 napi-rs 实现,对应的文档写得比较抽象,我是看 Rust 部分的文档反向推导 Node.js binding 该如何使用,期待文档的进一步完善。同时因为一些[已知问题](https://github.com/apache/opendal/issues/4782),暂时还无法直接在生产环境使用。
## Update 2024/10/16
距离上次更新又过去了 4 个月,这期间看了不少书,博文倒是没写几篇。这期间博客的源码倒是没少折腾,但基本都是细枝末节的修改,如例行的升级依赖啥的,比较需要注意的更新有如下内容。
### 补完全部文章的插图
博客的文章书写时间已经有 15 年的跨度,这期间文章的很多插图,要么尺寸过小,要么丢失。在 3 年前切换回 Wordpress 时进行了统一的梳理,将所有图片去除,转成了 Markdown 格式,但并未补全全部插图。这次借着切换为 Astro 的机会,将博客的文章都配上了精美的插图,并设置了对应的封面图片。(何等可怕的工作量)
### 支持动态 OG 图片
![Open Graph](/images/recaps/switch-blog-to-nextjs/open-graph.png)
对于 OG 图片的支持,历时 3 个月才予以实现,最早的版本为选取文章的封面,后面改为使用 [@vercel/og](https://vercel.com/docs/functions/og-image-generation) 生成。博客切换为 Astro 时一并放弃了 React Server Component 方案,对应的 OG 生成暂时搁置。在仔细学习了基于 napi 的 `@napi-rs/canvas` 后,本博客的 OG 图片使用 Canvas 绘制,并在构建的时候静态生成放于对象存储上来减少字体加载、渲染等耗时。目前 OG 的样式经过多次微调,暂时满意。
### 使用 Zeabur Serverless Function 部署
![Zeabur Astro](/images/recaps/switch-blog-to-nextjs/zeabur-astro.png)
Zeabur 因为有中国区,考虑网站面向的人群,我实在是无法割舍。之前因为使用 Astro Container API对应的包构建为 Lambda 时会有依赖问题,所以一直使用 Docker 方式在 Zeabur 部署。经过和 Zeabur 的小伙伴合作,同时完成了 Zeabur 对 `astro/env` 的支持后,现在网站已经不再使用 Docker 方式,转而使用 Zeabur 提供的 Serverless 的方式运行。
### 其他微调
* 文章页展示标签:标签更像是一个动态灵活的文章分类,之前设计新博客的时候有所遗漏,现在已经补上。
* 搜索结果支持分页:之前对 fuse.js 的理解不够深刻,经过仔细摸索,发现可以基于其结果做二次分页,于是不再限制搜索条数进行一页展示。
* 自定义鼠标样式:添加了箭头、手指的鼠标样式,并支持视网膜屏幕。
* 文章支持多别名:对于一篇文章,除了固定地址外,目前还支持了短链接别名。比如 https://yufan.me/papapa
--- ---
写此文章前,本有一肚子关于清明三天折腾的坎坷想要倾诉。真正写下来的时候,却又没多少。一来是年龄的增长,很多东西不再像以前那么过激。二来,大部分问题基本解决。数数上次更新博客的时间,已经不知道是猴年马月。希望自己在未来的年月里,能笔耕不辍,多记录一点自己的生活。 写此文章前,本有一肚子关于清明三天折腾的坎坷想要倾诉。真正写下来的时候,却又没多少。一来是年龄的增长,很多东西不再像以前那么过激。二来,大部分问题基本解决。数数上次更新博客的时间,已经不知道是猴年马月。希望自己在未来的年月里,能笔耕不辍,多记录一点自己的生活。

View File

@ -164,10 +164,3 @@ export const queryLikesAndViews = async (permalink: string): Promise<[number, nu
return results.length > 0 ? [results[0].like ?? 0, results[0].view ?? 0] : [0, 0]; return results.length > 0 ? [results[0].like ?? 0, results[0].view ?? 0] : [0, 0];
}; };
export const increaseViews = async (key: string): Promise<void> => {
await db
.update(atk_pages)
.set({ pv: sql`${atk_pages.pv} + 1` })
.where(eq(atk_pages.key, sql`${key}`));
};