面对 2015 年的老博客,是推翻重写还是爆改布局?借助最新 AI 模型结对,笔者仅用几个小时,完成了从前端排版到后端性能的全面现代化翻新。记一次时间性价比极佳的工程实践。
2026 年了。主站博客 (steinslab.io)伴随着笔者一路成长过来。11 年过去,当年的前卫审美放到今天最近的前端技术栈上,亟待翻修。
笔者原本觉得翻新老站是个无底洞,不如直接迁移到 Astro 等现代框架。但出乎意料的是,借助最新的旗舰 AI 模型结对,这台"现代化手术"仅花了我几个小时。这样,我就可以留更多时间给创作本身了!
全部工作只在 WordPress 后台的"附加 CSS"里完成,一行 PHP 模板都没动,也没加外部字体和 JS。 顺手还把后端 MySQL 和 PHP-FPM 的参数重新极限调优了一遍。
整个过程极其丝滑,主站可阅读性和性能极大提升,性价比极佳。这篇文章,就来盘点一下这次从前端到后端的翻新记录。
至于为什么不直接使用 Hugo 或者 Astro 现代化技术?笔者认为和旗舰模型结对重新开发迁移是非常可行的。但在 AI Coding 时代,保持一个良好的审美看来仍然是一件宝贵的技能。本身的排版、可读性,可能和后端是 wordpress 还是 astro 无关。
另外,如果读者也有基于传统 LNMP 搭建的 wordpress 博客,并且已经有比较多的内容,笔者强烈建议使用和旗舰大模型结对工作的方式进行翻新,整个过程会非常顺利且令人愉悦。
1 先体检:老主题到底哪里不对劲
笔者先把"哪里不对劲"拆成具体的条目。罗列得越细,后面修起来越不容易漏:
| 区域 | 问题 |
|---|---|
| 正文行宽 | 桌面端超过 900px,中文一行 60+ 字,眼球回扫距离过大 |
| 行高 | 约 1.5–1.6,反引号 inline code 会挤压上下文字 |
| 段间距 | 和行距差异不大,长文读到中段开始"糊" |
| 字号 | 正文约 15–16px,长文偏小 |
| 字体 | 未显式指定中文字体,Win 上退到雅黑,Mac 上是苹方,Linux 看缘分 |
| 反引号 code | 无底色无等宽,io_uring checkpoint 这种关键词和正文混在一起 |
| 标题 | H2/H3 只靠字号区分,没有颜色、装饰线、序号 |
| 首页卡片 | 日期 / 点赞数 / 热度 / 评论 / 作者 五项平铺,权重完全相等 |
| 侧栏 | 按月归档堆到 70+ 项,标签云字号乱飞 |
| 页脚 | 单行,底下还裸露着 wpDiscuz Insert 这类插件 DOM |
逐条对着看,读者大概能理解为什么笔者会产生"换衣服"的冲动。
这里面有的是字体问题,有的是布局问题,有的是信息权重问题,甚至有的是"插件漏到前端"这种尴尬的工程问题。接下来一块块处理。
当然,仅凭博主的美感是做不到这种洞察的。这些修改都是由 Claude Opus 4.6 提出的。看来在 AI Coding 时代,保持一个良好的审美看来仍然是一件宝贵的技能。
2 字体栈:先把最基础的统一起来
字体是一切排版的地基。笔者先从这里开始——它的收益最大,风险也最小。
2.1 为什么不选 Google Fonts
答案朴素:Google Fonts 的域名在国内访问不到。
fonts.googleapis.com 和 fonts.gstatic.com 。国内用户访问会经历:DNS 能解析 → TCP 连接超时 → 浏览器等大约 30 秒才 fallback。这 30 秒里,页面上所有字体相关的文字要么不可见(FOIT),要么一闪一闪。
老博客本身内容质量不差,不需要靠这种方式给读者添堵。
自托管也考虑过。中文字体全字库动辄 20MB 起步,想要观感好就得做 subset——按 Unicode Range 切片、用 fonttools 精简字符集、转 woff2、挂 font-display: swap。这一整套下来就是个小工程了。不做网络文字上投入,改为系统字体回退方案。
2.2 纯系统字体回退的设计思路
核心理念是:不加载任何外部字体,全靠浏览器逐字符匹配的能力,在不同操作系统上分别命中各自最好的字体。
中英文要分栈,这是 CSS 层面一个经常被忽略的事实:浏览器在遇到具体字符时,会按 font-family 列表依次寻找能渲染该字符的字体。英文优先匹配 Inter / SF Pro / Segoe UI,中文自动落到苹方 / 思源 / 雅黑。
最终用的字体栈大致这样:
--font-cn: "PingFang SC", "HarmonyOS Sans SC",
"Source Han Sans SC", "Noto Sans CJK SC",
"WenQuanYi Micro Hei", "Microsoft YaHei", sans-serif;
--font-en: "Inter", -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto,
"Helvetica Neue", Helvetica, Arial, sans-serif;
--font-mono: "SF Mono", "Cascadia Code", "Cascadia Mono",
"JetBrains Mono", "Fira Code",
Menlo, Consolas,
"Liberation Mono", "DejaVu Sans Mono", monospace;
它在各平台会命中的方式列出如下:
| 平台 | 中文命中 | 英文命中 | 等宽命中 |
|---|---|---|---|
| macOS / iOS | 苹方 PingFang SC | SF Pro | SF Mono |
| Windows 10/11 | 微软雅黑 | Segoe UI | Cascadia / Consolas |
| Android | 思源 Noto CJK | Roboto | Roboto Mono |
| Ubuntu / Fedora 中文环境 | 思源 Noto CJK | 系统默认 | DejaVu Mono |
| Arch 纯英文装机 | 文泉驿微米黑 兜底 | DejaVu Sans | DejaVu Mono |
Mac 用户看到的观感和系统 UI 一致;Windows 用户会差一档(雅黑在小字号下有轻微锯齿,但已经是 Win 上最好的选择);Linux 的情况取决于用户自己的配置——笔者专门把 WenQuanYi Micro Hei 加到中文栈末尾,给那些没装思源的老 Linux 用户留个兜底。纯英文 Linux 看到 sans-serif 兜底、可能还是豆腐块,但这类用户占比极低,且懂得自救。
全站零网络请求。 这是笔者最终选择"纯系统回退"的唯一理由。
2.3 修改对比
修改前
修改后
3 品牌与顶栏:辉光管 logo 和导航一体化
定完字体栈以后,笔者盯了一眼顶栏——还是那一行光秃秃的"团子云技术 – Steins;Lab"纯文字。后面正文区、卡片、侧栏、页脚都要动,但顶栏先成了视觉短板:和内容区摆在一起,它像是从旧模板直接撕下来贴上去的。品牌没有识别点、中英文平级、hover 只是底线变白——连"当前在哪一页"都看不出来。
从概念、SVG 到 nav 配色是一整条链路,和 Opus 4.6 结对设计的过程也很有意思。
3.1 方向:从"赛博朋克终端"到"辉光管实验室"
第一版的想法是命令行终端——深色底、霓虹绿、> prompt、闪烁光标。符合"技术博客"的刻板印象。
当时用 SVG 拉了一版示意,矢量稿放在同目录 blog-wordpress-refresh-terminal.svg。正文用 <img> 嵌入,Markdown 预览和站点都省事;img / background-image 下片内动画多半静止,直接打开 svg 文件或在页面里内嵌才能看到完整动效——和后文辉光管定稿遇到的情况一样。
画完以后自己看了一眼就否了:太通用。绿色终端这个审美从 GitHub README 截图到各种 devtools 主题,2018 年以后已经遍地都是。"团子云技术"这个博客名本身带着"研究所"的味道(Steins;Lab 致敬《命运石之门》里的"未来小工具研究所"),应该是更具体、更硬件的东西。
换了第二个方向——辉光管(Nixie Tube)。
辉光管是 1950–70 年代用来显示数字的气体放电管,玻璃管身里封着冷阴极造型的数字字符,通电以后整个字符会发出暖橙色的光。大学物理实验室、老式仪表、示波器面板上经常能见到。
第二版示意如下,矢量稿为 blog-wordpress-refresh-nixie.svg。同样用 <img> 嵌入;nixie-flicker 与光标动画在 img 里多半静止,直接打开 svg 可看到动效。
这个方向一下子对上了:
- 复古而具体:和 2026 年满街跑的"绿屏终端"拉开距离
- 硬件质感:和"技术博客"的定位吻合,比通用 IDE 配色更有个性
- 暖色:辉光管典型的
#FF5F00,在深色 nav 底色上非常显眼,但不像霓虹绿那么"卡通"
确定方向以后,剩下的就是把一只辉光管画进 SVG 里。
3.2 SVG 里画一只会发光的玻璃管
Logo 的左侧是一个辉光管图标,关键结构:
- 管身:
<rect rx="14">深黑填充#111,模拟玻璃在暗背景下的通透感 - 栅极横线:三条细线
<line stroke="#222">,对应辉光管内部真实的金属栅极 - 发光字符:一个橙色
>形状,用feGaussianBlur三重叠加做光晕 - 玻璃高光:前景叠一层
<linearGradient>,从左上 15% 不透明度渐到右下透明 - 底座 + 引脚:小黑长方形加三条金属立线,呼应辉光管的铜脚底座
效果示意
顺便,@keyframes 动画笔者最初是写了的(光标闪烁、辉光管电压不稳的微抖),但 WordPress 里 logo 是挂成 background-image 用的,浏览器出于安全策略会关闭 SVG-as-image 里的脚本和 CSS 动画。动画写了也不会播放,干脆移除。
3.3 中英文做成"横排单行"
第一版 logo 笔者做成了上下两行——CN 标题 + EN mono 副标题。
结果接到 nav 里明显偏方——aspect 比只有 2.5:1,一个短胖的小块,看起来不像"品牌条",更像一个按钮。
改成单行横排以后立刻舒服了。
效果示意
3.4 Nav 整体配色跟 logo 对齐
Logo 画完以后,笔者把整个顶栏的配色重新设计了一遍,让顶栏和辉光管 logo 连成一体,避免出现「黑条上硬贴一张图」的割裂感:
- 底色:
#0B0B0F → #15151B线性渐变,和 logo 玻璃管身呼应 - 菜单默认:
#CBD5E1slate-300,字距0.04em(中文菜单呼吸) - hover:文字变
#FF5F00辉光管橙,底线 2px 橙色 +box-shadow光晕 - 当前页 active:
>mono 前缀 + 橙色文字 + 橙色底线常驻,呼应 logo 副标题里的 prompt 字符 - 滚动态
.is-fixed:半透明深底rgba(11,11,15,0.82)+backdrop-filter: blur(14px),让底下内容透上来
所有元素围着同一个主题(辉光管 + 终端 prompt + 暖橙)服务,顶栏整体上像从 logo 里延伸出来的终端 title bar,和「黑条上多挂一块牌子」完全是两种观感。
修改前
修改后
这一段大概是整次翻新里笔者最享受的,是现在真的可以根据自己的需求,和模型结对做深度品牌设计。
4 正文排版:一行到底应该多宽?
字体定了,下面是中文技术长文的核心变量——行宽、行距、字号。这三个数字决定了长文读起来是不是累。
4.1 行宽锁在 42rem
中文的舒适扫视半径是一行 35–45 个字。按 17px 字号算,大约是 660–720px 宽。笔者用了 42rem ≈ 672px,配合两侧留白:
.k-main .details .article .content {
max-width: 42rem;
margin: 0 auto;
padding: 0 1.25rem;
font-size: 17px;
line-height: 1.85;
letter-spacing: 0.02em;
}
注意 line-height 给到了 1.85。17px × 1.85 ≈ 31.5px 的行高,对中英混排+内联 code 的场景足够宽松。太紧会挤,太松会散。
段间距比行高还要再大一点——margin: 1.4em 0。段落边界明显高于行距,读者扫的时候才能自然感受到"一个段落结束了"。这一点在 14000 字的长文里尤其重要。
4.2 标题给"装饰位"
标题只靠字号拉开距离是不够的。一个做法是给 H2 加一条 4px 的品牌色左竖线,H3 加一个带透明度的 # 前缀:
.content h2 {
padding-left: 0.7em;
border-left: 4px solid var(--brand);
font-weight: 700;
}
.content h3::before {
content: "#";
margin-right: 0.4em;
color: var(--brand);
opacity: 0.6;
}
这样扫一眼就能看出层级。代价不过是几行 CSS。
4.3 给内联 code 一个底色
技术文里信息密度最高的东西,是那些用反引号标出来的术语:fsync、io_uring、KV cache、checkpoint。它们如果没有视觉区分,基本就融进了普通文字里。
笔者的做法是底色+等宽字体+偏红的前景色:
.content :not(pre) > code {
background: #f6f8fa;
border: 1px solid #f0f0f3;
border-radius: 4px;
padding: 1px 6px;
font-family: var(--font-mono);
color: #c7254e;
}
修改前
修改后
改完之后重新去看那篇 FAST 长文——KV cache、checkpoint、prefix KV 立刻从段落里跳出来。这种"扫读能看到关键词"的体感,是技术长文必须给读者的。
5 首页卡片:告别老气横秋的"盒子感"
老主题的首页文章列表,带着一股浓烈的 2015 年气息。直角边框、生硬的实线分割、和标题挤在一行的分类标签。就是缺乏现代 UI 的"呼吸感"。
这次翻新,笔者没有停留在修修补补,而是直接对卡片的整体视觉骨架动了刀:
- 重塑层级与圆角:页面底色降级为
#fafafa,卡片保持纯白#fff。配合微弱的box-shadow和border-radius: 10px,卡片自然就从背景里浮出来了,不再依赖生硬的边框去框定边界。 - 分类标签"上树":原本的分类标签(如"项目跟踪")和标题挤在一行,视觉焦点混乱。笔者直接用
position: absolute把它做成带有品牌色投影的实心小胶囊,挂到了缩略图的左上角。 - 缩略图与细节:缩略图统一锁成 16:9 比例并加上圆角。底部的元信息(日期、评论等)分割线,从抢眼的实线换成了低调的虚线(
1px dashed)。标题字号也拉到1.3rem配合字重 700,让它在单栏布局里有足够分量。 - 解除高度封印:老主题在布局上埋了几个硬编码的雷,给标题和内容区写死了固定高度。一旦标题换行就会被生硬截断。笔者直接用
height: auto和max-height: none把这些封印全部解除,让内容自然撑开。
另外,为了统一视觉,缩略图被强制要求 16:9。
修改前
修改后
其实代码也就几十行,但改完之后,整个首页的观感立刻从"传统 CMS 列表"变成了现代化的阅读应用。
6 侧栏和页脚:做减法
老 WordPress 的侧栏像是 2008 年的产物。笔者的侧栏原本堆了 10 个 widget:作者卡、公众号、搜索框、最近评论、热门页面、分类、70+ 项的按月归档、字号乱飞的标签云、友情链接……
按月归档这个东西,2026 年还有谁会点?笔者自己都不会。
这块最终的策略是:
- widget 卡片化:每块独立白卡 + 1px 边框 + 小阴影,和主内容视觉统一
- widget-title 做成"小标签":13px 小字号、字间距 0.08em、全大写、底部 2px 深色实线
- 标签云字号锁死 12.5px:WP 自带的"按热度决定字号"那套审美收束掉
- 归档和分类条目过多时限高 280px 内滚动:保留存在性,但不让它吃屏幕
背景直接翻成 #18181b 深色,文字 #d4d4d8 浅灰。视觉一下子收束了——读者滚到页底会有"这篇内容到这里结束了"的明确信号。
7 服务端调优:2C4G 凭什么 OOM?
前面六节都在折腾前端 CSS。但翻新到一半,笔者想起来一个偶尔发生的、极度不符合直觉的事实:一台 2 核 4G 的机器,经常莫名其妙 OOM,MySQL 被系统强杀。
说来惭愧,整个的 LNMP 后端是作者若干年前一键安装的,中间几乎没怎么动过。虽说整个环境已经不是什么现代化设施了,2C4G 虽然不是什么神兵利器,但也绝不该被这点流量击穿。既然笔者已经从当年无知的高中生成长起来了,就没有理由让这些参数在拖硬件后腿。
排版再好看,页面 502 了也没用。既然都进手术室了,后端也一并处理。
7.1 PHP-FPM:堵住内存漏水的口子
原配置 pm.max_children = 18,数字看着保守,但致命的是没有设 pm.max_requests。
WordPress 的插件生态在内存管理上出了名的粗放。一个 PHP 进程刚启动时可能占 30MB,处理几百个请求之后不知不觉膨胀到 150MB 以上——内存碎片回收不掉,一路涨上去直到系统 OOM。
那在 4096MB 的物理内存里,PHP 到底能分到多少?笔者做了一次严格的内存预算:
| 开销项 | 预算 |
|---|---|
| Linux 系统 + Nginx | ~300MB |
| MySQL 峰值 | ~850MB |
| 其他自建服务(Gitea 等) | ~200MB |
| 系统 Page Cache(留给 Nginx 吐静态文件) | ~600MB |
| 剩余给 PHP 的预算 | ~2146MB |
按极端情况单进程 80MB 算,安全上限大约 个进程。最终配置:
pm = dynamic
pm.max_children = 25
pm.max_requests = 500
pm.max_requests = 500 ——一个 PHP 进程处理完 500 个请求后强制自杀,主进程重新孵化一个干净的新进程。从物理上斩断了内存泄漏的无序累积。 代价是第 501 个请求的访客多等几十毫秒的进程重启延迟,生产环境里完全可以忽略。
作为第一门语言是 Go 的开发者,看到 PHP 竟然还能自动经过请求数来杀进程,深表震惊。真的不知道前辈们究竟经历了什么……
7.2 MySQL 5.7:卸掉沙袋
原配置的 MySQL 可以用"营养不良还背着沙袋跑步"来形容。笔者翻了一遍 my.cnf,发现了三个典型问题:
performance_schema 是内存刺客。 这玩意是 MySQL 的性能监控表,一个博客站根本用不上。但它启动就会凭空吃掉近 400MB——在 4G 的机器上,这是要命的比例。
query_cache 是时代的眼泪。 老配置还开着 64M 的查询缓存。WordPress 这种高度动态的系统,表里更新一个瞬态数据,MySQL 就会瞬间锁住并清空相关缓存。高并发下,频繁的"锁-清空"循环反而引发排队卡顿。MySQL 8.0 已经直接把这个功能删了,说明官方自己也觉得弊大于利。
DNS 反向解析在浪费时间。 默认配置下 MySQL 会对每个连接的 IP 做反向 DNS 解析。本机直连的架构根本不需要这一步,网络一抖动就会导致连接无故卡顿数秒。
最终在 my.cnf 里做的调整:
[mysqld]
# 关闭性能监控,夺回 ~400MB
performance_schema = off
# 挤出来的内存喂给 InnoDB
innodb_buffer_pool_size = 512M
# 关闭查询缓存
query_cache_type = 0
query_cache_size = 0
# 斩断 DNS 解析等待
skip-name-resolve
# 降低磁盘 I/O 压力
innodb_flush_log_at_trx_commit = 2
关掉 performance_schema 之后,innodb_buffer_pool_size 终于有底气拉到 512MB。绝大多数查询直接在内存里命中,CPU 的 iowait 立刻降下来了。
innodb_flush_log_at_trx_commit = 2 需要单独说一下取舍:它让每次事务提交先写入系统缓存,每秒刷盘一次。如果宿主机突发断电,可能丢失最后 1 秒的事务。但对博客系统而言,这个微小风险换数倍写入性能的提升,笔者认为是划算的。
7.3 Swap:两害相权取其轻
原机器的 vm.swappiness = 0,意思是宁愿 OOM 也不碰硬盘。在纯数据库节点上这是对的,但在 Web + DB + 杂七杂八服务共存的混合型机器上,其实是个陷阱。
设为 0 的后果:一些长期休眠的"死代码"——比如几周没人访问的 WordPress 后台管理模块——死死霸占物理内存,真正需要内存的热路径反而挤不进来。
设为 10 的效果:Linux 在内存用到约 90% 时,把这些冷页面挪到 swap 里。腾出来的物理内存会被系统自动转化为 Page Cache。Page Cache 是 Nginx 吐静态文件的超级引擎——图片、CSS、生成的 HTML 缓存直接从内存飞出去。
vm.swappiness = 10
这是一种防御性策略。遇到极端突发流量,系统仍然可能短暂假死。但这总好过 OOM Killer 直接把 MySQL 砍掉,全站 502 还得人工干预。
8 后记:主次先于观感
所有改动加起来大约 800 行 CSS 加一轮服务端参数调优,笔者和 Claude Opus 4.6 结对,几个小时就做完。没有装任何新插件、没有换主题、没有加外部依赖。
改完之后把那篇 14000 字的 FAST 长文重新用手机和电脑各读了一遍。行距舒缓、KV cache checkpoint 这些术语从段落里自然浮出、段落之间有节奏。主观感受是——终于可以心平气和地读完自己写的东西了。
笔者这波改下来,排版上最先动手的永远是把文章的主次说清楚;主次顺了,页面通常也就顺眼:
- 正文是主角,所以它居中、窄列、字号合适、字距舒缓
- 标题是分段路标,所以它有色条、有前缀、视觉重量明显高于正文
- 术语是扫读的锚点,所以反引号 code 要给它底色和等宽字体
- 卡片元信息是配角,所以低信息价值的项目就不该出现
对于人类修改,真的要很耗时间。现在和模型结对编程,它们全部落实成 CSS 规则和配置文件的修改、在一个十年老博客上推下去、顺便把几个响应式断点下的老 hack 和祖传参数一并修掉——就是一次完整的现代化翻新了,整个过程丝滑愉悦。
受限于笔者的背景和主题限制,目前的版本还有不少可以继续打磨的地方:顶栏搜索框没做、首页 Hero 没做、按年归档折叠没做。这些都需要动主题 PHP,属于"下一阶段"的工作。不如就直接更换为 Astro 或者 Hugo 的静态技术栈。
纯 CSS 加参数调优能做到的那一层,已经让这个 2015 年的博客在 2026 年看起来不那么尴尬了。这对笔者来说够用了。
如果有读者也在用"看起来有点年头"的 WP 主题,希望这篇能帮上忙。欢迎一起交流排版和调优的心得——这东西没有标准答案。欢迎读者反馈新的页面视觉感受。
号外:作为独立博客,我想我是永远不可能放弃主站的。欢迎有兴趣的读者和团子交换友情链接,也欢迎访问 团子云技术 - Steins;Lab。