文章

六月琐事与小项目:自己开发的一些项目以及一些碎碎念|闲情杂记

六月琐事与小项目:自己开发的一些项目以及一些碎碎念|闲情杂记

六月过得有点碎。高中开学之后,能整块坐在电脑前折腾的时间越来越少;可手里又确实攒下不少零碎的事:自己写的一个静态网站生成器、博客的反复搬家、字体和排版上的微调,还有对代码托管平台的一通整理。中间还夹着刷闲鱼时看到的种种怪现状。这些东西彼此没什么关联,索性合在一篇里,当作六月的杂记。

一、自己开发的静态网站生成器:Zest SSG

Zest SSG 当前全称为 Zest: Easy Static-site Toolkit;此前的全称 Zealous Efficient Static Toolkit 已弃用。Zest SSG 本质上是一个混用 F# 与 C# 的静态网站生成器。其核心理念只有一条:模板应当是真正的代码,而不是字符串模板;编写模板的语言应当与主语言一致。

这一设计源于对现有静态站点生成器的不满:许多工具要求使用 YAML 配置,并采用基于字符串替换的模板语法。项目作者日常使用 F#,因此希望构建网站时能够直接执行 F# 脚本。为此,Zest 的页面文件采用 .zest.fsx,即真实的 F# 脚本,构建时通过 dotnet fsi 执行;可使用列表推导、模式匹配、字符串插值等 F# 语言特性。

样式方面,Zest 不引入 Sass、Less 等外部依赖,而是设计了 ZCSS。ZCSS 是 CSS 的超集,支持嵌套、F# 风格的 let 绑定、数学表达式、颜色函数和 mixin,并最终编译为标准 CSS。例如,可写 let space4 = 0.25r * 4,先完成计算,再将结果用于样式。

配置格式仅使用 TOML,不使用 YAML。项目可包含可选的 _init.zest.fsx,在构建前执行,用于注入动态数据,并可读取 JSON、TOML 和环境变量。

目前已具备的能力包括:

  • 模板即代码:.zest.fsx 是真实的 F# 脚本。
  • HTML DSL:声明式组合 HTML。
  • 内联 Markdown:可在脚本中直接混写 Markdown。
  • 标准 .md 文件:支持 frontmatter。
  • ZCSS:支持嵌套、变量、数学表达式和颜色函数。
  • TOML 配置:默认零配置可用。
  • 构建能力:热重载、批量评估(多个脚本在同一进程中运行)、增量构建、跨平台。

快速开始仅需几行命令。

1
2
3
4
zest init my-site
cd my-site && zest serve --port 8080
zest build
zest preview

举个具体例子,.zest.fsx 里可以这样写:

1
2
3
4
5
6
7
let pageTitle = "Hello from F#"
let items = ["F#"; "Zest"; "SSG"]
render [
    h1C [ text pageTitle ]
    pC  [ text "This page is generated by real F# code at build time." ]
    ulC [ for i in items -> li [ text i ] ]
]

ZCSS 的例子:

1
2
3
4
5
6
7
8
9
let primary    = #3b82f6
let space1     = 0.25r
let space4     = space1 * 4
let primary-light = primary |> lighten(45%)
.tag
  color: $primary
  background-color: $primary-light
  padding-block: $space4
  border-radius: 9999px

ZCSS 的编译产物是标准 CSS。

架构上,Zest 分为四个部分:Zest.App(C# 命令行入口)、Zest.Engine(F# 核心逻辑)、Zest.Dsl(F# 预编译辅助)和 Zest.Infra(C# 配置管理与文件监听)。这种拆分是有意为之:用 C# 实现命令行入口更便捷,用 F# 实现计算逻辑与 DSL 更合适,从而让各语言发挥各自优势。

以下设计原则为有意坚持:

  1. 以 F# 编写模板:模板本身就是程序。
  2. ZCSS 是布局引擎,而非传统预处理器:其产出为 CSS。
  3. 以 TOML 作为配置文件,绝不使用 YAML:这一点尤为坚定,因为此前曾深受 YAML 缩进问题困扰。
  4. JavaScript 仅负责客户端交互:不引入 Node、npm 或打包器。
  5. 目标用户是少数喜爱 F#、反感 YAML、偏好简单工具的人。

此后 Zest 更新至 v0.3.1,这是首个稳定版本。性能有明显提升,但 FSI 冷启动仍需约 2–3 秒,无法避免;Zest Page 与 ZCSS 语法已基本可用。不过,本月将暂停更新,原因是学业安排。目前该 GitHub 组织仅有我一名成员,暂无其他管理员协助维护项目。

二、给博客换一套骨架:从 Chirpy 到 Eleventy,又回到 Jekyll

(顺带一句:这套 Eleventy 博客后来我又迁回了 Jekyll,那是后话;但当时的折腾值得记一笔。)

之前我的个人博客用的是 Jekyll + Chirpy 主题,走本地构建再推送的路子,省去了平台构建不一致的麻烦。但那套方案有个绕不过去的限制:Chirpy 主题打包在 Ruby gem 里,能改的只有配置文件和有限的几个 include。想动布局、改色调、换字体,在 Chirpy 的体系里都不是“改一行配置”能解决的事。Jekyll 本身又绑定在 Ruby 生态里,插件写起来也不算顺手。

搬到 Eleventy 的理由很简单:它不绑定模板语言,不绑定 CSS 方法论,连输出格式都不绑定,你想要什么自己搭。副作用是,一切都要自己搭。但正是这种“自己搭”的模式,让 11ty 成了最接近裸写 HTML 的静态站点生成器——模板里写什么标签,它就输出什么,不会在你不知情时往页面里塞多余的 div、inline script 或 CSS class。对于想精确控制每一行输出的开发者来说,这种体验无可替代。

整个项目分三个层次。最底层是数据层,src/_data/ 目录下放着一堆 JSON 和 JS 文件:site.json 定义站点名称、时区、每页文章数等全局参数;author.json 存个人信息和社交链接;comments.json 保存 Giscus 和 Twikoo 的配置;taxonomy.js 做中文字段到 URL-safe slug 的映射。另有两个计算数据文件 sidebarCategories.js 和 sidebarTags.js,在构建时扫描所有文章,统计分类和标签频率,输出给右侧栏使用。

中间是逻辑层,一个 .eleventy.js 文件处理所有构建逻辑:插件注册(RSS 生成、代码高亮)、过滤器(日期格式化、阅读时间估算、HTML 剥离、分页导航的 prev/next 计算)、集合定义(文章按日期排序、分类聚合、标签聚合),以及最重要的搜索索引生成。

搜索索引是一个特殊集合——searchIndex。它不在模板中渲染成页面,而是在构建时遍历所有文章,读取每个 Markdown 文件,剥离 YAML frontmatter,清除 HTML 标签和 Markdown 标记,截取前 1500 个字符,最后输出成一个 search.json 供客户端使用。这样做的好处是索引在构建时即已确定,用户打开搜索时只需下载这个预先生成的 JSON,服务端不用跑任何查询。

最上层是表现层。布局模板在 src/_layouts/ 下,页面路由在 src/routes/ 下,组件在 src/_includes/ 下。CSS 全部分散在 src/assets/css/ 下的六个文件里:variables.css 存放所有设计变量(颜色、字体、间距、阴影、圆角),base.css 做 CSS reset 和排版基调,layout.css 定义三栏网格和页面结构,components.css 覆盖所有组件样式(卡片、徽章、分页、侧边栏、搜索弹窗、文章导航、TOC 等),theme.css 做表面装饰(噪点纹理、悬浮效果、入场动画、回到顶部按钮),responsive.css 专门处理响应式断点。

没有用任何 CSS 框架,Bootstrap、Tailwind、Bulma 一概没有。每一个像素都是手写的 CSS 自定义属性。

布局上,左侧栏固定 240 像素,包含作者卡片、导航菜单、一个每秒更新的数字时钟,还有一个 JavaScript 生成的当月日历;右侧栏固定 260 像素,包含自动生成的目录、站点统计、分类列表和标签云;中间主内容区自适应填满剩余空间。这套布局在桌面端表现不错,到平板上切成两栏——隐藏右侧栏,左侧栏保持固定;手机上则完全放弃侧栏,左侧栏变成从左滑出的抽屉菜单,由汉堡按钮控制。断点选在 1200px 和 768px:前者是常见笔记本屏幕的宽度下限,后者是 iPad 竖屏与手机的分界线。

配色上,浅色模式以柔钢蓝为主色调(#5a8fb4),配暖灰背景(#e0e8f0)和白色卡片(#ffffff);暗色模式切换到暖琥珀色(#e8915a),背景用深灰(#18181b),卡片用略浅的深灰(#222225)。四个辅助色分布在不同 UI 元素上:分类标签用墨绿系,标签云用暖棕系,侧栏装饰用薰衣草系,代码块用土橘系。用户切换一次主题后,选择会存进 localStorage,下次打开直接复用,不依赖系统偏好。

还有一个刻意的细节:所有圆角都设为 0。直角在中文排版里比圆角更接近传统印刷的质感,尤其是配合衬线字体做标题时,直角边框和规整的网格更能呼应纸媒的气质。

搜索功能用 Fuse.js 做客户端模糊匹配。构建时生成 search.json,结构是一个数组,每篇文章包含标题、URL、日期、摘要、标签、分类,以及截取的正文前 1500 个字符。用户在搜索弹窗输入关键词后,Fuse.js 在浏览器端做模糊匹配,权重分配为标题 50%、摘要 25%、正文 15%、标签和分类各 5%,阈值 0.35,允许单字符匹配——这对中文搜索很重要,因为中文没有英文那样的天然空格分词。

JavaScript 全部手写,没有用 jQuery 或其他框架,功能包括:移动端抽屉开闭、返回顶部按钮显隐(滚动超过 300px 出现)、阅读进度条(按 scrollY 与可滚动总高度的比值更新宽度)、标题锚点(为正文中每个标题生成可点击的链式图标,点击后复制完整 URL 到剪贴板)、目录生成(从 h2/h3 提取标题构建 TOC,滚动时自动高亮当前章节)、主题切换、数字时钟、当月日历(支持前后翻月),以及搜索弹窗的全部交互逻辑。

Service Worker 在生产环境下注册,预缓存站点资源,实现离线访问;manifest.json 中定义了 PWA 的基本配置,包括启动画面、主题色和应用名称。

代码推送到 GitHub 后,由 GitHub Actions 自动部署:先把 _site 目录上传为 GitHub Pages 的 artifact,再调用官方 deploy-pages action 部署。此外还配了两个推送目标:一个是推到 GitLab 做镜像仓库,另一个是克隆 Codeberg Pages 仓库,把 _site 内容复制进去,写入自定义域名文件,再推送回 Codeberg。三个平台共用同一份构建产物,输出完全一致。Build.ps1 是 Windows 下的构建脚本,逻辑很简单:删掉旧的 _site 目录,执行 npx @11ty/eleventy。

所有文章使用同一个 frontmatter 结构。_posts.json 为 _posts/ 目录下的 Markdown 文件设置默认值:布局为 post.njk,永久链接格式为 /posts//。每篇文章的 frontmatter 至少包含标题、日期、分类和标签;摘要可选,不填的话首页卡片显示正文前几行。

评论系统同时支持 Giscus 和 Twikoo。comments.json 里配置两个 provider 的参数,post 布局底部的 comments.njk 组件根据 provider 字段,选择渲染 Giscus 的 client.js 脚本还是 Twikoo 的初始化脚本。两者可以同时启用,读者自由选择。

路由方面,除了文章详情页,还生成首页分页列表、分类索引页、分类详情页、标签索引页、标签详情页、归档页、关于页、友链页、留言页和分享页。这些页面共用 base.njk 布局,各自独立渲染内容区。

从 Jekyll 到 Eleventy,最大的感受不是某个具体功能的好坏,而是控制权的转移。在 Chirpy 主题下,你只能做主题允许的事;在 Eleventy 上,每一个决定——CSS 变量怎么命名、布局怎么分层、搜索索引权重怎么分——都是你自己的。代价是每件事都要自己动手。喜欢开箱即用,Chirpy 仍是更好的选择;想完全掌控博客的每一层,Eleventy 值得花时间。

EXYONE@BLOG ~$

三、字里行间的较真:字体与首行缩进

最近调整了博客字体,正文从更纱黑体换成小米兰亭。(9 月更新:现在换成了思源黑体,兼容性更好一些。)

更纱黑体我用了挺久,它融合了思源黑体和 Iosevka,现代感强、字符齐全,但读起来的体验总不如系统默认字体舒服。换了小米兰亭,对比才明显:它的字母更粗、更圆润,整体观感柔和,长时间阅读不容易累。小米兰亭设计时就考虑了阅读感受,笔画清晰、灰度均匀,用在博客正文里,字与字之间的呼吸感刚好。

不过字体效果很大程度取决于系统渲染。Windows 的字体渲染一直是个老问题,ClearType 再怎么调,比起 macOS 的原生渲染总像隔了层纱,换什么字体都难有质的改善。iOS 这边不论什么字体,线条清晰、边缘平滑。Linux 上,至少 KDE Plasma 开箱即用的渲染不错,字形和间距都舒适;其他桌面环境我没深入用,不作评价。

字体选择也不能光看个人偏好。博客标题原本用繁体,觉得有古风质感,正文用简体便于阅读,但朋友常吐槽看不懂标题。后来换字体,一直找不到华英明朝体的感觉,折腾半天才发现:ZSFT 默认提供的华英明朝体本有简体版本,却默认调用 T 字符集的繁体版,而非 Classic 简体。改过之后,标题回归简体,质感稍差一点,可读性却大大增强。

说到底,字体没有绝对的好坏,只有合不合适。如果博客以正文为主,又想在 Windows 和 macOS 之间找平衡,不妨试试小米兰亭——至少目前,它是最让我舒服的阅读字体。

人气很高的霞鹜文楷,在我看来更像手写楷体而非标准印刷楷体,在计算机字体分类里接近艺术字。很多人喜欢它比印刷体多出的温度与活力,但艺术字用于正文并不理想,风格也和我的博客调性不符。

另外,Linux 默认字体大多从文泉驿系列转向了思源系列。思源黑体中庸平稳,比略显稚气的文泉驿微米黑成熟。以前缺好开源字体,发行版才偏爱文泉驿;如今多数发行版和桌面环境都已内置思源宋体、黑体。

排版上还处理过一个看似微小、却影响阅读气质的细节:中文正文的首行缩进。

起初做法简单粗暴,给所有 <p> 标签加 text-indent: 4em;。视觉上段落整齐右移四格,有了传统印刷品的秩序感。但问题很快出现:滚动到列表或引用块时,由于结构常是 <li><p>,里面的段落也被缩进,显得错位。这让我明白不能一刀切,得分清边界。

Halo 主题(以及多数现代 CMS)为了语义化,常把列表项文本包在额外的 <p> 里,直接选 p 会误伤。解决办法是划定范围:放弃全局 p,改用排除选择器锁定正文段落。

1
2
3
4
5
p:not(li p):not(blockquote p) {
    text-indent: 2em;
    margin-top: 0.8em;
    margin-bottom: 0.8em;
}

这样只缩进直接属于内容的段落,列表和引用内的不受影响,排版恢复逻辑。过程中还定了几点准则:一是用 em 而非 px,响应式设计中固定像素不随字号变化,em 能自动伸缩;二是移动端微调,小屏上过宽的缩进会挤压阅读空间,因此加媒体查询,屏幕小于 480px 时缩进从 2em 减为 1em。

最终只用了一段不依赖 JavaScript 的 CSS,未改动 HTML 结构,完成了视觉秩序校正。这种“只动样式、不动结构”的思路,不只适用于 Halo,对 Typecho、WordPress 和静态博客生成器同样通用。不过后来因为显示 Bug 较多,本站已放弃正文段落首行缩进。

四、不把鸡蛋放在一个仓库:GitHub 之外的代码仓库托管选择

客观地说,到 2025、2026 年,GitHub 的问题已不再只是“偶尔抽风”,而是在多个方面频繁影响正常使用:免费 Actions 分钟数被大量机器人或挖矿账号占用,导致正常用户排队时间明显变长;安全漏洞修复偏慢,供应链攻击时有发生;国内访问不稳定,CI/CD 经常卡顿,部署成功率如同抽奖。更令部分用户不满的是,微软似乎对免费用户关注不足——企业版销售火热,免费用户却相对被忽视。于是,越来越多人开始寻找替代方案,国内平台、海外服务和自建方案都会被纳入考虑。

国内平台普遍要求强制实名认证并绑定手机号,这是难以回避的门槛。若将几家常被提及的平台放在一起比较,各自的定位与取舍会更加清楚。

平台背景优势短板适合谁
Gitee(码云)国内访问速度快、稳定性高,功能基本齐全——Pages、Wiki、Issues、PR、Release、Actions 都有,Actions 虽弱但能用;可外链项目卡片,免费私有仓库多,中文社区完善Actions 算力有限,免费额度用尽便无法使用,敏感内容可能被要求整改人在国内、看重日常开发和速度,唯一靠谱的国产主力
GitLinkCCF 和中科院背书,完全开源支持 Pages、Wiki、Issue 等名字容易和 GitLab 混淆,社区活跃度低,Issue 响应慢,国内访问一般学术圈可考虑,日常开发还是 Gitee 更稳
Coding(腾讯云)大厂背书企业级服务稳定,CI/CD、项目管理、代码托管一体免费额度少,付费门槛高,界面臃肿,操作繁琐,数据隐私也存疑企业用户可考虑,个人开发者不建议
Codeberg德国非营利组织运营,完全开源,数据受 GDPR 保护Pages 功能强,支持 Hugo、Jekyll 等静态生成器;社区友好,人工客服响应快国内访问需要代理,社区规模也小追求开源精神和隐私,这是首选
GitLab功能齐全CI/CD、项目管理、托管一体,自托管版免费商业化重,免费版受限,国内访问慢更适合企业用户
SourceHut走极简路线,无广告无追踪,完全开源极简、无广告、无追踪界面简陋,新手不友好,国内也需代理极简主义者可选,不适合大众
Gitea轻量,资源占用低,完全开源Pages、Wiki、Issue 都支持代价是自维护服务器和备份有服务器资源的开发者可以首选
OneDev实现 DevOps 一体——托管、CI/CD、项目管理,轻量开源托管、CI/CD、项目管理一体,轻量开源同样需要自己维护有服务器资源的开发者

GitHub 不再是唯一选择:国内用户可以用 Gitee 做主力,海外选 Codeberg 追开源,有服务器就自建 Gitea 或 OneDev。重要的是找到适合自己的,而不是被单一平台绑定。

五、闲鱼上看到的怪现状

闲鱼是个神奇的地方。这里几乎什么都有,黑市敢卖的它敢卖,黑市不敢卖的它也敢卖。抛开那些乌烟瘴气的,单从程序员视角看,在闲鱼搜“GitHub”,出来的商品就让人意外。最夸张的莫过于刷星、自动发货的账号交易、PR 造假、源码代下载,以及各路商家晒着疑似脚本刷出来的贡献墙,明码标价兜售“大学生作业代做”——什么编程语言都接,装得跟技术大牛似的,买的人居然还不少。

我知道不少大学生进了大学就放松放纵,但说句实话:玩归玩,闹归闹,课别天天翘,哪怕进去听一丁点儿呢。这纯属交冤枉钱。而且这些商家大概率也是拿 AI 做的项目,花同样的钱买 Token,至少能学会怎么用 AI、怎么 Vibe Coding,总比彻底躺平强。抖音、B 站上铺天盖地都是 AI 焦虑的视频,另一头却有大学生心甘情愿把钱送给闲鱼上的无良商家。我实在怀疑我们不在同一条时间线上——既然学了计算机,网上一点行业动态都不刷吗?2023 年 AI 还没成气候也就罢了,今年都 2026 年了。

更让我想不通的是,Java 和 C++ 代做也就忍了,连 Python 项目都有人找代做。问题早就不在难度上:现在随便一个网页端 AI Agent(比如智谱清言 GLM、MiniMax)都能开发 Python 软件,Django、Flask 这些框架也不在话下,还全免费。稍微了解一下当下 AI 的发展现状,就知道这钱没必要花。这种“能花钱解决就绝不费脑”的思维,说到底是得过且过,并不是什么明智的生存策略。

这类网友我见过一些,也有认识的朋友:比如某程序开发专业的大学生,在某个开源项目的视频底下问 Git 是什么、GitHub 为什么打不开;还有我在某个 Q 群里认识的网安专业老哥,大学混了三年,光顾着玩,电脑都没正经碰过。古有指鹿为马,今有指 Linux 为华为系统,而 Windows,则被誉为“安卓电脑”。

如果说上面那是糊涂,接下来的操作就是使坏——利用开源项目牟利。闲鱼上那些刷 GitHub 贡献墙的商家,稍微搜一下就知道,这东西就是 GitHub 上的开源工具 GreenWall(绿墙),想刷多少贡献随便填,全刷绿都行。可即便如此,依然有人花高价买它去“优化”简历。项目地址在这里。

我也凑了个热闹,花十几块钱分别买了个刷星服务和一个新账号尝鲜。刷星服务倒是挺“专业”:那些假号有模有样,有 Issue、有 PR、有仓库还有 Fork,还分批刷,估计是为了躲风控,这点确实有一手——但刷星总归是不提倡的,懂的都懂。听说 OpenClaw 就是刷星刷出来的项目,微软那边基本睁一只眼闭一只眼。至于那个新账号,说实话没什么用,真不如手动注册。大家想看可以去这瞧瞧:https://github.com/exyone-dev。我已经换上自己的 ID、密码,绑定了邮箱和 2FA。总结一句:除非账号自带 GitHub Pro,否则没必要买,不太划算;哪怕想要个资历更老的号,也用不到这东西——上面那个绿墙工具就能实现,在十几年前的年份刷贡献,那一年就会出现在贡献墙上,账号看起来自然更老。

更新:买的这个账号似乎已经被 GitHub 云控了,现在无论是提交 PR 还是创建 Issue,别人都看不到。

六、张雪峰、计算机专业,与一群找不到北的新生

张雪峰是个极具争议的人物,但不得不指出:计算机专业的混子数量之多,和张雪峰脱不了干系。两三年前他力荐计算机专业,大量大一新生涌入,质量参差不齐是一方面,另一方面,计算机专业对学习能力的要求极高。那时候他的话被很多家长奉为圭臬——尤其是那些自身毫无主见、控制欲却很强的家长。他们只记住了“计算机好就业”,对专业内容、行业门槛、孩子特质一概不知。

更令人唏嘘的是这批学生的家庭背景:很多孩子从小到大几乎没正经碰过电脑,刚进大一,家长反倒大手一挥配齐“华为全家桶”。让一个对计算机毫无基础、心智尚未成熟的孩子,突然闯进一个极度依赖自学能力和信息素养的领域,本身就埋下了隐患。这些新生接触到外面的世界后变得浮躁,沉不下心学习。

两三年前,网上还有人会说:“计算机和起点无关,穷人家的孩子只要肯学,照样能出头。”这话没错,我也支持;但现实是,绝大多数人并没有那么好的自控力。中小学阶段接触不到外面花花绿绿的世界,还能保持朴素心态,一旦踏入大学,诱惑扑面而来,能守住本心、不随波逐流的人少之又少。好比把一只从小关在笼里的鸟突然放飞,却指望它依然安分地在铁栏里歌唱——未免太理想化。计算机专业对学习能力要求极高,因为技术栈只会不断更新迭代,没人能靠 jQuery 和 BootStrap 干一辈子不失业。如今再没人敢轻易劝人学计算机,原因很实在:一个几年前人挤人的专业,稍有常识的人都看得出来,迟早该饱和了。

本文由作者按照 CC BY 4.0 进行授权