- 前端脚本从 jsDelivr 改为自托管 public/assets/js/twikoo.min.js, 版本与后端对齐(1.7.20)。国内访问 jsDelivr 实测 3 秒以上且常被墙, 是评论区空白的根因 - 新增 public/assets/css/twikoo-custom.css 主题适配层:选择器加 #twikoo 前缀以覆盖运行时注入的样式,颜色引用主题变量,自动跟随亮/暗色 - 修复首屏重复初始化:astro:page-load 在首屏也会触发,用 dataset.mounted 去重 - 评论配置移除已无用的 version 字段;README 与操作指南同步更新
261 lines
10 KiB
Markdown
261 lines
10 KiB
Markdown
# 时之刃的小站
|
||
|
||
个人博客源码 —— [tianxuetao.top](https://tianxuetao.top)
|
||
|
||
一个基于 Astro 的静态博客,主题为 [VermilionVoid](https://github.com/Lapis0x0/VermilionVoid)。除了常规的文章系统,还有碎碎念、读书/论文/漫画书架、学习追踪与加密文章。
|
||
|
||
站点配置集中在两个文件,改站名或换域名通常只需要动第一个:
|
||
|
||
| 文件 | 管什么 |
|
||
|---|---|
|
||
| `src/data/site.ts` | 站名、域名、描述、关键词、主题色、统计分析 |
|
||
| `src/data/profile.ts` | 昵称、签名、头像、社交链接 |
|
||
|
||
评论配置在 `src/data/comments.ts`。
|
||
|
||
> 日常操作(写文章、发偶得、加照片、部署)看 **[docs/操作指南.md](docs/操作指南.md)**,那份更偏"照着做";本文偏项目结构与原理。
|
||
|
||
---
|
||
|
||
## 技术栈
|
||
|
||
| 层 | 选型 |
|
||
|---|---|
|
||
| 框架 | [Astro](https://astro.build) 5(`output: "static"` 纯静态) |
|
||
| 交互组件 | React 19(islands)+ Radix UI + Tailwind CSS 4 |
|
||
| 代码块 | Expressive Code(行号、折叠、语言徽章、自定义复制按钮) |
|
||
| 数学公式 | remark-math + KaTeX |
|
||
| 站内搜索 | Pagefind(构建时索引) |
|
||
| 页面切换 | Swup |
|
||
| 部署 | `./deploy.sh`(rsync 到树莓派 + 云服务器) |
|
||
| 评论系统 | 自托管的 [twikoo](https://twikoo.js.org)(见下) |
|
||
|
||
包管理器是 **pnpm**。
|
||
|
||
## 快速开始
|
||
|
||
```bash
|
||
pnpm install
|
||
```
|
||
|
||
```bash
|
||
pnpm dev
|
||
```
|
||
|
||
| 命令 | 作用 |
|
||
|---|---|
|
||
| `pnpm dev` | 本地开发服务器(默认 `http://localhost:4321`) |
|
||
| `pnpm build` | 构建到 `dist/`,并跑 Pagefind 建立搜索索引 |
|
||
| `pnpm preview` | 本地预览构建产物 |
|
||
| `pnpm check` | Astro 的类型与内容校验(本项目没有单元测试框架,这就是唯一的自动检查) |
|
||
| `pnpm new-thought "..."` | 快速创建一条「偶得」(见下) |
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
src/
|
||
├── pages/ 路由入口(每个文件对应一个 URL)
|
||
├── layouts/ 页面骨架
|
||
├── components/ Astro / React 组件,可复用的原子组件放 components/ui/
|
||
├── content/ Markdown 内容集合(见下)
|
||
├── data/ 站点配置:站点信息、个人信息、相册、评论、友链
|
||
├── styles/ 全局 CSS
|
||
├── plugins/ 自定义 remark / rehype / Expressive Code 插件
|
||
├── lib/ hooks/ 支撑逻辑
|
||
public/ 原样拷贝到产物根目录的静态资源
|
||
├── assets/images/ 文章配图
|
||
├── gallery/<id>/ 相册照片,构建时自动扫描
|
||
scripts/ 内容工具脚本
|
||
```
|
||
|
||
## 写作
|
||
|
||
内容集合定义在 `src/content/config.ts`,共三个:
|
||
|
||
### `posts` —— 正式文章
|
||
|
||
放在 `src/content/posts/`,支持子目录分类。
|
||
|
||
```yaml
|
||
---
|
||
title: 文章标题
|
||
published: 2026-08-17
|
||
updated: 2026-08-18 # 可选
|
||
description: 摘要 # 可选,用于 SEO 和卡片
|
||
image: /images/cover.webp # 可选,封面
|
||
tags: [Astro, 前端]
|
||
category: TechnicalTutorials
|
||
pinned: false # 置顶
|
||
draft: false # 草稿不会被构建
|
||
---
|
||
```
|
||
|
||
**加密文章**:给 frontmatter 加上 `encrypted: true` 和 `password: "你的密码"`,该文章正文会被加密,需输入密码才能阅读。适合半公开的内容。
|
||
|
||
**免责声明**:`disclaimer` 字段可以引用 `src/data/disclaimers.ts` 里预设的声明文案。
|
||
|
||
### `thoughts` —— 偶得
|
||
|
||
短内容,放在 `src/content/thoughts/`。只有 `published` 是必填,其余可选。
|
||
|
||
```yaml
|
||
---
|
||
published: 2026-09-14T12:36:47.215Z # 必填。务必带时区,见下方说明
|
||
title: 可选标题
|
||
location: 张掖 # 可选,显示在日期旁
|
||
weather: 晴 27°C # 可选
|
||
images: # 可选,public 下的绝对路径
|
||
- /gallery/GalleryFav/某图.webp
|
||
tags: [生活]
|
||
pinned: false # 可选,置顶排在最前
|
||
---
|
||
```
|
||
|
||
用脚本创建更省事:
|
||
|
||
```bash
|
||
pnpm new-thought "换了散热,温度降了 15 度。" --location 张掖 --tags 硬件 --pinned
|
||
```
|
||
|
||
> **`published` 一定要带时区**(`2026-09-14T12:36:47Z` 这种)。写成不带时区的 `2026-09-14 12:36`,YAML 会按 UTC 解析,显示时会偏移。偶得页按 `src/data/site.ts` 的 `timezone` 渲染,所以带时区的时间才会显示正确。
|
||
|
||
### `studies` —— 专题
|
||
|
||
```yaml
|
||
---
|
||
title: 主题
|
||
status: 在读 # 在读 | 沉淀中 | 暂搁 | 已结
|
||
started: 2026-08
|
||
field: 领域 # 可选
|
||
subtitle: 副标题 # 可选
|
||
epigraph: 题记 # 可选
|
||
---
|
||
```
|
||
|
||
## 相册
|
||
|
||
照片放在 `public/gallery/<相册id>/`,构建时**自动扫描目录**,不需要在配置里逐个列出。
|
||
|
||
新增一个相册 = 两件事:
|
||
|
||
1. 在 `src/data/gallery.ts` 的 `albums` 里加一项(`id` 要和目录名一致)
|
||
2. 建目录 `public/gallery/<id>/` 并放照片
|
||
|
||
```ts
|
||
{
|
||
id: "GalleryFav",
|
||
name: "喜欢的壁纸",
|
||
description: "相册描述",
|
||
location: "甘肃-张掖",
|
||
date: "2026-07-18",
|
||
tags: ["壁纸", "收藏"], // 用于索引页筛选
|
||
}
|
||
```
|
||
|
||
封面优先级:`cover` 字段 > 目录里的 `cover.*` 文件 > 第一张图。
|
||
|
||
> 照片是原样下发的静态资源,**不经过 Astro 优化**。分辨率高的图建议先压成 WebP 再放进来,否则单页可能几十 MB。
|
||
|
||
## 自定义
|
||
|
||
想把这个博客改成你自己的,主要改这几处:
|
||
|
||
| 想改什么 | 改哪里 |
|
||
|---|---|
|
||
| 站名、域名、描述、主题色、统计 | `src/data/site.ts` |
|
||
| 昵称、签名、头像、社交链接 | `src/data/profile.ts` |
|
||
| 相册 | `src/data/gallery.ts` + `public/gallery/` |
|
||
| 评论(twikoo) | `src/data/comments.ts` |
|
||
| 友链列表 | `src/data/friends.json` |
|
||
| 首页打字机文案 | `src/components/hero.tsx` 顶部的 `phrases` |
|
||
| 导航栏、页脚 | `src/components/site-header.astro`、`src/components/footer.tsx` |
|
||
| 配色与全局样式 | `src/styles/global.css` |
|
||
| 代码块主题 | `astro.config.mjs` 里 `expressiveCode` 的 `themes` |
|
||
| 免责声明模板 | `src/data/disclaimers.ts` |
|
||
| 头像、favicon 等静态资源 | `public/` |
|
||
|
||
代码风格:Astro/TS/TSX 一律 2 空格缩进。组件命名 `PascalCase` 与 `kebab-case` 混用,跟着所在目录的既有风格走即可。项目没有配 formatter,改动尽量贴合周围代码。
|
||
|
||
## 书架数据
|
||
|
||
书架页的数据来自这三个文件,由 `src/components/bookshelf-content.astro` 在**构建时 import**(所以文件必须存在,删掉会导致构建失败):
|
||
|
||
- `public/data/books.json`
|
||
- `public/data/papers.json`
|
||
- `public/data/comics.json`
|
||
|
||
目前都是空的 `{"data":{"items":[],"total":0}}`,所以书架页显示为空。要填充就按同样的结构往 `items` 里加记录。
|
||
|
||
## 部署
|
||
|
||
主用 `./deploy.sh`,构建后 rsync 到树莓派和云服务器:
|
||
|
||
```bash
|
||
./deploy.sh # 构建 + 部署到两个目标(覆盖生产前会确认)
|
||
./deploy.sh --pi-only # 只推树莓派(内网预览)
|
||
./deploy.sh --cloud-only # 只推云服务器
|
||
./deploy.sh --dry-run # 只显示 rsync 会做什么
|
||
./deploy.sh --skip-build # 不重新构建,推现有 dist/
|
||
./deploy.sh -y # 跳过确认
|
||
```
|
||
|
||
目标地址和 SSH 密钥在脚本顶部的**配置区**。脚本会在构建失败或产物异常时**直接中止**——因为推送用的是 `rsync --delete`,拿一个残缺的 `dist/` 上去会清空线上目录。
|
||
|
||
`vercel.json` 仍然保留,把它接到 Vercel 也可以。走 Vercel 时需要在项目里配置环境变量 `PUBLIC_TWIKOO_ENV_ID`(评论后端地址,留空则不渲染评论区);本地开发写进 `.env`(已 gitignore),参考 `.env.example`。
|
||
|
||
---
|
||
|
||
## 评论系统
|
||
|
||
使用 [twikoo](https://twikoo.js.org),后端为自托管实例。
|
||
|
||
配置在 `src/data/comments.ts`:
|
||
|
||
```ts
|
||
export const commentConfig = {
|
||
twikooEnvId: "https://twikoo.tianxuetao.top",
|
||
lang: "zh-CN",
|
||
stripTrailingSlash: true,
|
||
}
|
||
```
|
||
|
||
`twikooEnvId` 留空则整个评论区不渲染;也可以用环境变量 `PUBLIC_TWIKOO_ENV_ID` 覆盖,优先级更高。
|
||
|
||
**前端脚本不走 CDN**,而是自托管的 `public/assets/js/twikoo.min.js`。国内访问 jsDelivr 很不稳定(实测 3 秒以上,且常被墙),走 CDN 会让评论区长时间空白甚至加载不出来。该文件的版本要与后端保持一致(当前都是 **1.7.20**,可用 `GET_FUNC_VERSION` 查后端版本),升级后端时记得同步替换它。
|
||
|
||
主题适配样式在 `public/assets/css/twikoo-custom.css`。twikoo 的样式是运行时注入 `<head>` 的、会排在后面,所以那里的选择器统一加了 `#twikoo` 前缀来提高优先级,颜色引用主题变量因而自动跟随亮/暗色。
|
||
|
||
前端逻辑在 `src/components/Comments.astro`,处理了两件事:
|
||
|
||
- **切页重新挂载**:本站启用了 Astro 的 `ClientRouter`。注意 `astro:page-load` 在**首屏也会触发**,所以用 `dataset.mounted` 标记防止重复初始化;切页后是新容器、没有该标记,会正常重新挂载。
|
||
- **评论归属路径**:`trailingSlash` 是 `"always"`,但评论 key 统一去掉结尾斜杠。浏览器提交的是**百分号编码**过的 `location.pathname`(如 `/posts/%E7%94%9F%E6%B4%BB/...`),后端按原样存储和匹配 —— 所以去斜杠后的编码路径正好与历史数据一致。改掉这条规则,老评论就会对不上。
|
||
|
||
评论出现在:文章页、关于页、友链页。
|
||
|
||
主题原作者的自托管评论后端 `cwd-api/` 已随迁移移除。
|
||
|
||
---
|
||
|
||
## Fork 须知
|
||
|
||
如果你要基于这个仓库搭自己的博客,至少改这几处:
|
||
|
||
1. **`src/data/site.ts`** —— 站名、域名、描述、主题色,以及 `analytics` 里的统计 ID(每一项留空就不加载对应脚本)。`astro.config.mjs` 的 `site` 已经引用这里的 `site.url`,不用单独改。
|
||
2. **`src/data/profile.ts`** —— 昵称、签名、头像、社交链接。
|
||
3. **`src/data/comments.ts`** —— twikoo 后端地址,填你自己部署的实例。
|
||
4. **`src/data/friends.json`** —— 友链列表。
|
||
|
||
另外这几处仍是从主题原作者仓库带过来的内容,按需清空替换:
|
||
|
||
- `src/content/` 下的文章、碎碎念、学习专题
|
||
- `public/data/*.json` 的书架数据
|
||
- `public/images/vote/` 的赞赏收款码(关于页已不再引用)
|
||
|
||
## 致谢
|
||
|
||
文章系统的部分设计参考了 [Fuwari](https://github.com/saicaca/fuwari),主题为 [VermilionVoid](https://github.com/Lapis0x0/VermilionVoid)。
|
||
|
||
## 许可
|
||
|
||
代码部分可自由参考取用。`src/content/` 下的文章内容版权归作者所有,转载请注明出处。
|