
HY
折腾进行时
Hello, I'm HY.








这份清单回答一个问题:这个站到底改了哪些东西。
博客是 fork 的 CuteLeaf/Firefly,从 V1.0 到 V1.49 一共 51 版。下面按板块把所有功能列出来,每条附一句它干什么、关键数字是多少。逐条的完整背景、明细和日期都在站内的更新日志页(现在 V1.49、51 个更新日、248 条明细),封面和正文里的截图都是从站上实拍的。

left 不能按等距算,各张基准宽度不同、图片在盒子里居中,按 left 等距排会让视觉中心最多漂 ±11px(1920 宽下)。data-* 属性、不发图片请求,脚本给抽中的那几张赋 src 才开始下载。手机端只显示 4 张,之前是 12 个请求 / 149 KB,现在就是那 4 张。−,点它立刻收(用减号不用叉,因为这按钮是「先收起来」不是「永久关掉」);收起后右边缘挂出一根竖排小签(默认「关于我」),点它、按回车或空格都能把卡片滑回来,带 aria-expanded。z-index 压在箭头上面,居中那张正好把箭头盖死。没去动箭头层级(那样屏幕中央会多出一颗压在贴纸身上的圆钮),改成让贴纸让路:底部这排从素材里随机抽 10 张、左右各 5 张分列,正中央空出来。#main-grid 的 grid item 横跨左右侧栏,这样点下滑箭头滚到它时不会被甩出视口,塞进文章列又会太窄。DOM 默认 display:none、数据在页面渲染完之后才异步拉,拉不到整条不显示;实测掐断接口后 FCP 仍是 1112ms、CLS 为 0。/moments/(V1.1)—— 把所有启用友链的最新文章聚成一条时间线,按时间倒序、支持分页。构建期抓各站 RSS/Atom,友链配置里没填 rss 的会去探常见路径和首页的 <link rel="alternate">;抓取结果写成本地快照兜底,单站失败不阻断构建。页面里另有一张「随机文章」卡片,每 5 秒换一篇,本站标蓝徽章、推荐友链标金徽章。快照刷新走 GitHub Actions 定时任务,每周六 06<00>00> 跑一次并自动提交。/analytics/(V1.12)—— 七个模块:四色总览卡、访问脉冲(7 / 30 / 90 天切换 + 手动刷新 + 数字滚动)、推送节奏热力图(按 GitHub 提交时间,带贪吃蛇动画)、访客活跃时间、设备与浏览器环形图、地域分布(中国地图 + 流量来源占比)、Waline 评论统计。数据走 Umami 公开分享接口、浏览器直连,取数要同时带 x-umami-share-token 和 x-umami-share-context 两个头,少一个就 401。#2d7cff 换成 var(--primary),kicker、小标题、折线图、时间范围切换一次性全部跟随。推送数从 1695 修正到 252(仓库是 fork,git log 不加 --author 会把上游 CuteLeaf/Firefly 的历史全算进来);推送热力图改用 GitHub 原版绿(这块本来就在模仿贡献图,跟主题色反而怪),「访客活跃时间」那块仍跟主题色;两个状态角标从右上角挪进标题块 —— 那两处 header 都是 space-between,右侧恰好都是角色插画,角标一直被顶上去压住它们。/bangumi/(V1.16 / V1.17)—— 最早开的是 /bilibili/(构建期拉追番追剧清单,221 条、均分 8.7),后来关掉换成番组计划:浏览器实时拉 api.bgm.tv 的收藏,动画 308 部、看过 277 部。改成访客侧拉取是刻意的 —— 构建期不请求任何外部接口,收藏更新也不用重新部署,代价是首屏靠 JS 渲染。封面图床有防盗链,得把 *.bgm.tv 加进 noReferrerDomains,不加会整页裂图。/steam/(V1.25)—— 拥有游戏数、总时长、近两周时长、等级徽章,以及按时长排序的全部游戏列表。两条取数路径自动切换:有 STEAM_API_KEY 走 Web API 拿完整列表,没有就退回 steamcommunity 公开 XML 的精简模式(只有常玩几款,快照打 partial 标记、页面上给提示)。封面图按 apps/<appid>/header.jpg 直接拼,省掉「先拉商店详情拿 hash」这一步。main.css 挪进这一页,只有 /guestbook/ 加载。137 个新文案走自建语言表,没动 src/i18n/。pid / reply_user 字段,判据用 typeof pid === 'number',因为一级评论的 pid 是 null 而不是缺失);移动端公告栏被左上「留言板」、右上「成员」两个悬浮按钮压住(给会话区垫出 3.25rem 让开浮层式头部);桌面端面板顶边正好贴着首屏壁纸下沿、太挤(往下压 2rem,只改 margin-top 不动高度)。公告弹窗还加了「不再显示」,写进 localStorage 之后该访客不再自动弹。
/blog-changelog/ —— 就是这份清单的来源:每条改动记一行版本号、一句话摘要、背景说明和明细。V1.32 给它瘦过一次身:「背景」段落收进折叠区,默认只留版本徽章和标题,顺手把最长的两条描述从 1835 字压到 246 字。/dynamic/rss.xml(V1.24)—— 动态页有了自己的订阅源,和页面同一份数据(当前 12 条),每条跳回 /dynamic/ 对应锚点。动态页头部「N 动态」旁边注入了一个 RSS 按钮(小屏只留图标),head 里同步埋了 rel="alternate"。/booknav/ 的卡片悬停展开该站首页截图:卡片从 flex 改成 grid,封面独占一整行、高度 0 → 7rem 展开,图片首屏不加载,真悬停 / 聚焦过才请求。截图是离线跑脚本批量截的,带拦截校验 —— 渲染后 HTML 的前 4000 字符命中 Cloudflare 403 / 验证页特征就丢弃,再用 sharp 算通道标准差、纯色空白页也丢弃,否则悬停展开会是一张白图。/friends/ 才按需拉。referrerpolicy="no-referrer",解决 BlogsClub 头像 404;它那张头像另外改成本地托管,不再依赖对方站点资源。enable-card-border 开关、本站早就开着,只是边框色用了 --line-divider(亮色下是 8% 的黑,淡到看不出轮廓)。换成纯黑、宽度 2px,样式走页脚注入。难点全在补清单:文章卡片、侧栏组件、分页按钮先统一,自建页再逐个补(留言板主面板与两个弹窗、Steam 四类卡片、朋友圈的三种变体、书签导航、友链卡片、Bilibili 卡片、更新日志页);顺便发现有几类卡片 border 只写了宽度和颜色、没写 border-style,那样是画不出来的。暗色下自动切纯白。代价是悬停时边框不再变主题色,反馈改由位移和阴影承担。window.load 之后的 requestIdleCallback 上,不进首屏关键路径;取数失败时整行保持隐藏,既不显示 0 也不显示「加载中」。material-symbols:train-rounded(列车正面,呼应页脚徽标里的车头造型)。上一版「省空间所以只留文字」的理由在这档宽度已经过期:紧凑模式的选择器会命中所有下拉容器的直接子项,开往同样被隐藏,实测余量仍是 1024px +40px、1280px +194px,一字没变 —— 图标只影响 ≥1600px,那里余量本来就够。.wl-quote 的直接子节点 —— Waline 点「回复」时插进来的编辑框不是这个类,不会被误伤;观察器挂在 body 而不是 #waline,因为后者会随无刷新切页被整个替换掉。wl-reply-to 里那个 a 标签的 href 就是被回复评论的 objectId,而每条评论节点的 id 正好也是它 —— 顺着锚点把目标找回来,摘成引用块插在下面;点它会滚到原评论并闪一下,目标正被折叠收着时先自动展开到那一条。V1.44 又瘦了一次身:原生那行「@昵称:」不再显示(引用块里已经写明回的是谁),引用块去掉底色只留左边一根主题色竖线、前面补一枚 ↩ 符号,浅底色挪到回复正文上。:root 放桐乃、html.dark 放黑猫),28 条规则只写一遍、切主题只换变量;每条三档声明覆盖高分屏,代码块则保留系统原生 I 型光标。FIXED_CITY。全站显示,桌面挂右栏 top 容器末尾、小于 768px 换挂到移动端底部组件栏,跨断点自动搬运;默认展开,点一下压到 9.5rem 只露地点与温度,状态记在会话存储里。接口有配额(1500 credits / 31 天),所以本地缓存给到 30 分钟。两个坑:参考站那套图标是 Astro 构建期按需渲染的 symbol、动态插入的节点取不到,最后把 34 枚图标的 path 内联进脚本;指标网格的 auto-fit 在 280px 右栏算下来只排得出一列,把卡片顶到 925px,改成固定两列后落到 613px。s-maxage=86400 边缘缓存(浏览器端保持 max-age=0,发新文章立即可见);/_astro/ 哈希资源缓存一年并标 immutable,重新部署自动换哈希无需手动刷新;/assets/、/pagefind/、/pio/ 缓存 30 天;/api/ 边缘缓存 1 小时、浏览器不缓存。另加一个部署后自动从 sitemap 提取全站 URL 预热边缘缓存的工作流。stale-while-revalidate=604800:CDN 缓存过期后先给访客旧页面秒开,后台同时拉新页面更新缓存。max-height: 104px 夹到约 78×104),8 张从 331 KB 降到 127 KB;修掉「装饰层入场动画要等 9 张图全部加载完才开始」的阻塞(改成只等卡片自身的图,兜底超时从 1400ms 收到 400ms);favicon 从 88 KB 换成 8.9 KB —— 原来那个 .ico 其实是一张 221×183 的 PNG 改名的,重新生成了真多尺寸 ICO。首屏合计少传 283 KB。opacity: 1,只保留 12px 的位移)后延迟从 0.36 秒砍到 0.08 秒,入场逻辑也拆成两段 —— 文字动画不再等图片。<template> 标签里,浏览器解析 HTML 时根本看不到它,必须等内联脚本把它克隆进 DOM 才开始下载。fetchpriority=high:一个小头像被标成高优先级,而真正的首屏标题和壁纸反而只有默认优先级。同时给 /images/ 和 /favicon/ 补上 30 天缓存头,这两类之前落在默认规则上、响应头是 max-age=0, must-revalidate,等于每次访问都要重新校验一遍。pagefind.js、worker、wasm、索引分片严格串行依赖,而它只在用户碰到搜索框时才开始加载,几跳的等待全落在输入之后;二是取数请求数和结果条数同阶,主题对全部结果逐个调 data(),而每个 data() 就是一次 fragment 请求,搜个常见词命中 45 条就是 45 个请求。现在新增一个预热脚本:首屏空闲或鼠标碰到搜索框时就把 worker 起来、wasm 编译好、索引读进内存;再用一层 Proxy 只让前 6 条真拉正文(面板本来就只显示 5 条),实测 45 → 6,结果数组长度不变所以「查看更多 N 条」的计数仍然准确。顺带补上「回车 = 进第一条结果」(isComposing 为真时放行,不干扰中文输入法)。移动网络与省流模式下跳过预热,不抢带宽。/analytics/ 页面里的 7 张插画归档成新相册:页首横幅、两张角色立绘(保留透明通道)、四张概览卡片插画。三张 PNG 转 webp(质量 90、不放大、保留透明通道),四张本来就是 webp 的原样字节拷贝,避免二次有损压缩。public/images/cursors/。这类原样直出的相册不另建 gallery-originals/ 副本 —— 那个目录只给压过 webp 的相册用。src/site-content/ 进 .gitignore,从此合并上游不用再为文章目录解冲突。构建前脚本从 CONTENT_REPO_URL 浅克隆内容(--depth=1),本地已有内容时自动跳过;本地开发走 CONTENT_DIR 加增量同步脚本,dev.sh / dev.bat 一键完成「同步 → 后台」。.gitignore 里,而 Tailwind v4 的自动内容探测会跳过被 git 忽略的路径 —— 于是「只出现在内容文件、主题源码从没用过」的工具类永远不会生成,类名照旧挂在 HTML 上、规则却不存在。解法是加一个登记表:同步脚本顺带扫内容里出现的 class 名、写进一个没被忽略的文件,内容仓那边一个字都没改。processor.render() 是异步的、返回 { code, metadata },之前把 Promise 当 HTML 字符串往下传,走到剥标签那步就抛 html.replace is not a function。更麻烦的是上游适配器里那句 promise.finally() 派生出的 promise 没人接管,fetch 一失败就是未处理的 Promise 拒绝,在 Node 24 下直接把构建进程带走,调用方的 try/catch 拦不住。现在构建期先做一次带 4 秒超时的可达性探测,连不上就完全不调接口;另外对 Memos 请求失败产生的未处理拒绝加了窄范围兜底,只忽略超时、连接重置这类网络错误并打印告警,其它拒绝照旧抛出。if (coverUrl) 直接跳过,静默不展开、零报错。所以把体检塞进那个静态 JSON 路由:比对配置与清单,缺了就 console.warn 列出来并附补图命令,零联网、零耗时。这五十一版几乎全部落在 src/config/**、public/** 素材和新增的独立文件上 —— 跟上游的实际差异是 522 个新增文件加 66 个改动,而真正落进组件和页面目录的只有 5 个(CoverImage / WallpaperSection / Profile / friends.astro 加了 1 行 / guestbook.astro),剩下的全是数据文件(github-card-data.json / lqips.json)。原因很实际:上游更新勤,改动挂在外面才好合并,出问题也能整段摘掉。
这套「挂外面」的做法最近又救了一次场:动态页那条链路要补的是全站评论增强,而动态的评论跑在 iframe 里、那个页面没有页脚 —— 换作直接改页面或组件,就得跟上游的 comments.astro、DynamicInlineComments.astro、dynamic-inline-comments.ts 三个文件纠缠;挂在页脚注入上,只多了一个新脚本就解决了。
逐条的完整明细还是看更新日志页。
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
部分内容可能已过时
分享你的想法,与大家交流讨论