这是我的部落格,宙と木。每一次换页,都是一颗琥珀色的点从这一页飞到下一页,落进下一页自己的那条线。10 月 25 日凌晨,它在诞生的第 572 天换上了这个样子。
这个站一开始只是我自己从零拼起来的作品集,之后每多一页就照当时的感觉补一页。做了一年左右,我开始觉得它冒出一股 AI 味,很多地方看起来像随便哪个模板都长得出来,所以今年五、六月参考了 innei 部落格的几个做法,后来又换上 WebGPU 画的星空背景(那篇在这里)。文章、碎念、看过的动画、拍的照片都放在这里,所以我一直希望它看起来是「我的」,而不是某个主题改一改。
老实说,半年前我对这个 blog 还算满意。只是我一直是一页一页分开改的,从来没想过整个站放在一起是什么样子。四月改「成长轨迹」页的时候,我把卡片背板拿掉、换成背景浮着的巨大年份,看起来是好看了,可是总觉得缺了点什么。后来才想通,缺的是沉浸感:每一张卡片都像独立的投影片,中间没有任何东西把它们串起来。
我平常是个很常放空、看着天空发呆的人,也一直觉得自己的内在比这个站能装下的宽很多,只是不知道怎么表达。直到十月某天我把音乐、书柜、设备三页并排看,发现它们长得一模一样:大标、副标、往下一排卡片。那一刻我觉得这些页面只是为了展示而放在那里,每一页都长这样的话,读者要探索什么?我决定一次翻掉。
「从现在起,我要让地球动起来。」
《地。关于地球的运动》
这是我很喜欢的一部作品里的台词。故事里的人跨越好几个世代,冒着生命危险把地动说一路传下去。我做的事当然小得多,但下定决心要把整个站翻过来的那一刻,脑中浮现的就是这句话。
最后花了四天。10 月 22 日开始想方向,25 日凌晨上线,合并的那个 PR 动了 449 个档案,加 41,744 行、删 53,184 行。十四个页面全部重画,每一页换成一个天体或一种太空里真的会用到的东西。这篇写的是这四天,和这四天之前的 572 天;转场和字型的技术细节收在文末的附录,想自己动手做的人可以直接跳过去。
第一版,失败了#
一开始我没有先画设计稿,想说直接在本机改出来比较看得出效果,原型跟实际搭背景的感觉差很多。第一版保留了写实材质的土星,换页时让行星爆炸碎成星尘,手记的卡片往下长到页面底部接上地平线,碎念页做成像素风。
做出来我自己看了很不满意。土星移动时会过曝,爆炸散开的粒子跟背景原本的星星接不起来,看起来像萨诺斯弹指,又像橡皮擦把东西擦掉。手记的星轨一开始密到我看了头晕,内容几乎被盖掉;版面怎么调都有种上个时代部落格的味道,很像以前的 Yahoo 奇摩。
改了一个下午,每次修完一个地方,另一个地方就显得更不搭。问题在于我没有先定基调,每一页各自加特效,材质、配色、动态都在各说各话。老实说那版也不算丑,只是不是我要的,所以我没有删,封存在 GitHub 的 archive/sky-morph 分支,然后回头从方向开始想。
「不正确,不代表没有意义。」
《地。关于地球的运动》
这句话在《地。》里反复出现。第一版是错的,但如果没有先做错,我也不会知道自己真正想要的是什么。
只留点、线和一点琥珀#
我做 app 的时候本来就偏好衬线字那种质感,站名「宙と木」的「宙」也本来就是宇宙(「木」来自我的网名木村盆栽),所以方向其实很快就收敛了:星空本来就可以只用点和线画,星是点,轨道、星轨、彗尾都是线,用线把页面串起来,刚好补上我觉得缺少的那份沉浸感。
最难割舍的是首页的土星。那颗有材质建模的土星,我一直调到今年四月,效能、画质、整体观感都磨过好几轮,要整个抛弃真的舍不得。最后我把它留着,但换成点和线的画法:一条条白色刷痕画在不透明的球上,环也是线。换完之后效果好很多,我才发现自己不一定要那么坚持真实的材质。不过舍不得还是舍不得,旧的那颗我留在旧站里当纪念。
定下来的规则很少:
| 项目 | 决定 |
|---|---|
| 颜色 | 黑 #0A0A0C 当纸、暖白 #F1ECE3 当墨、琥珀 #E9C17A 只当「一个点」的讯号 |
| 材质 | 不用卡片、阴影、毛玻璃,能不用分隔线就不用 |
| 中文字 | 思源宋体(Noto Serif TC) |
| 英文字 | 只用在整段英文的大字,用 Instrument Serif,一律不加粗 |
我对调色没什么概念,但很确定一件事:颜色一多,画面就没有主体了,所以整站只有那颗点是琥珀色的。这次我也学乖了,先把方向想清楚再写程式。每一页我都先做成可以直接在浏览器里点的示意,一版一版改,前后改了 15 版,全部页面都定案之后,我才开始动网站。
每一页都是一个天体#
方向定了之后,每一页我都问同一个问题:这个东西在太空里会是什么?每页的副标都是我自己写的,我想让整站读起来有一点文青味,但不要冗言赘字。
| 页面 | 副标 |
|---|---|
| 首页 | 一个写代码的人,偶尔造点东西。 |
| 手记 | 群星在流转,给每一瞬的好奇留下一道弧线。 |
| 碎念 | 捕获一道划破寂静的杂音。 |
| 作品 | 在无声的深空里,凿出一寸寸痕迹。 |
| 收藏 | 引力所及之处,皆是照亮过我的碎光。 |
| 照片 | 跨越虚空的微弱信号,在此逐行显影。 |
| 设备 | 解构精密,安放偏执。 |
| 朋友 | 深空漫长而孤寂,所幸回头总有星芒。 |
| 留言 | 长夜无声,静候来自远方的脉冲。 |
| 航行日志 | 挣脱引力,此身即是长空。 |
| 关于 | 坐稳了。 |
| 此站点 | 此处无他,唯有黑夜、白墨与微光。 |
首页:什么都不放#
首页是那颗土星,手记、碎念、作品、收藏、关于五个分区是绕着它转的五颗卫星。我刻意不在门面放任何资讯,平常只有线和点,滑鼠靠近哪颗卫星,那条轨道才亮起来,浮出那一区的名字和最新一则;闲着的时候,偶尔会有一颗自己亮一下,提醒你它们是可以点的。我希望进来的人是自己去探索的,就像我在这个宇宙里探索自己一样。
土星我坚持留 3D,是因为之后想让首页变成一个可以在里面遨游的立体空间。
碎念:一闪而过的彗星#
碎念对我来说是脑海里闪过的一些瞬间,像 Threads 那样随手一发,所以做成一颗彗星。它沿着一条大 S 弧线飞,最新的那一则是彗核,旧的沿着彗尾左右排开,很旧的只剩尾巴上的一个点,滑过才浮出来。
点进单则碎念时,镜头会往彗尾上那一段放大进去,像用望远镜拉近,那一段亮起来、其他的退暗,底下的留言就是那段彗尾散出来的碎屑。我想要的是「你在看彗星的某一段」,而不是跳到另一个页面。
手记:长曝光的星轨#
手记是我最喜欢的一页。一篇文章就是一道星轨:越新的文章绕在越外圈,写得越长,弧就拖得越长。背景那些淡淡的弧也是星轨,所以整片夜空看起来像一张长曝光的照片。打开页面时像快门刚打开,所有的星一起拖出轨迹,之后整片绕着天极慢慢转。星轨其实是地球自转留下的痕迹:天上的星星没有在转,是我们站着的这颗星球在转。
最让我满意的是点进文章的那一下:你点的那一篇,它的星轨会被拉直、拉近,变成文章页右边那道轨道。
这道轨道同时是目录。章节是轨道上的刻度,刻度之间的距离照章节实际的长度排;读过的部分是细实线,还没读到的是点线,琥珀点就是正在读的你。手记列表是一圈一圈的星轨,点进一篇,等于镜头拉近到其中一道。
设备:还不满意的图纸#
设备页是改到最后还是不太满意的一页。第一版是零重力组装,加上太空船子系统的标签和即时遥测,做出来很平淡,而且跟宇宙其实没什么关系。我心里一直想把它做成工程概念设计图,但自己一笔一笔刻要花太多时间,最后是用 Gemini 生白底黑线的爆炸轴测图当底稿,再由程式叠上编号圈、引线、尺寸线和零件表;滑过编号,底稿上那个零件就染成琥珀色。
第一张图纸 SHEET 0 是老家那台大概 2012 年买的电脑。它没有照片,机壳是凭记忆画的点状草图,点会微微晃,规格像开机自检一样一行一行打出来。它是我的童年,所以放在最前面。
这一版我还是不满意,之后应该还会再改。
还有几页#
其他几页也都各有一个天体:
- 作品是一条从中心往外长的螺旋航线。起点放的是毕业专题 VoltiCar,一个把碳权游戏化的专题;选题不太理想,分组也不太顺,现在回头看技术上就是小儿科,可是它是我第一个把当时会的东西全部用上的专案,前后做了一年多,常常做到废寝忘食。所以它是航线的起点。每件作品是一颗线画的小星球,大小、地貌、环和卫星都由 GitHub 的数据长出来。
- 照片照着太空照片传回地球的流程画:探测器拍下来转成 0 和 1,由深空网路的大碟子收下来,一行一行显影成黑白,最后才上色。
- 朋友的中心是这个站,朋友是绕着它转的星,点一颗就跟他对接,两艘船接上,旁边展开他最近写的文章。
- 留言的每一则都是一道收到的讯号,打字时你打的字会即时变成波形,送出时飞进天线。
- 航行日志是一整页升空的场景,离地的高度是累积的 GitHub 贡献,这页在文章最后还会再出现一次。
- 此站点是一具六分仪对着星空,里面的玩具一律拿站上真的东西,一步一步拆开写出当初怎么想的。我希望有人玩完会觉得「原来也可以这样做」。
关于:就此落下#
关于页是自由落体。点从正上方慢慢掉下来,停成「坐稳了。」的那个句号,读者往下卷,就是跟着它一起往下坠。
灵感是之前在新闻上看到的一支影片:葡萄牙的深夜,一颗蓝色火球划过天空,整片夜空被照亮好几秒。天文现象一直很震撼我,一颗星落到地球,就跟人生一样,散发出最绚丽的光芒之后转瞬即逝。所以这一页掠过那些稚嫩和迷惘的昨天,停在「就此落下」。但落下之后才是新的开始,再往下是地底,一层一层挖到我做过的事,最深的地方放着我最喜欢的一句话。
「我想,感动大概比寿命的长度还要重要。」
《地。关于地球的运动》
收藏:改了四次的讯号#
收藏页(在看、书柜、在听)是改最多次的一页,因为我想在这里展示的是品味:让人看到我看过很多东西,也看得出我喜欢什么。第一版三区并排,太重复;第二版改成切换加星尘重组,做出来太刻意;第三版是三团星云,往下卷镜头在三团之间飞,还是没有宇宙浩大的感觉,圆形的封面也被切掉大半。
三版都不对之后,我不再想「封面要怎么排」,改成想:在太空里,这些东西实际上会怎么被用到?答案是讯号。每一部作品是我看完那一刻从地球发出的一圈讯号,离地球多远,就代表离现在多久。往下卷,是从地球一路往外退,经过真实位置的月球、韦伯望远镜、行星、航海家一号、太阳圈边界、欧特云,最后退到大约 9 光年外的邻近恒星。
往外退的路上会经过航海家一号。1990 年,它在大约 60 亿公里外回头拍了一张地球,地球在照片里还不到一个像素。卡尔·萨根看着那张照片写下:
「再看一眼那个小点。那就是这里,那是我们的家,那就是我们。」
卡尔·萨根,《暗淡蓝点》
我想让收藏页有这种感觉:往外退得越远,地球就越小,而那些照亮过我的作品,都是从那个小点发出去的讯号。
一颗点,串起每一页#
如果整个翻新只能保留一件事,我会选转场。我一直觉得转场的丝滑和衔接度,决定了一个站有没有质感:读者不一定说得出哪里好,但卡一下、闪一下、位置跳一下,他们一定感觉得到。
我对「顺」的在意不是这次才开始的。七月改文章页载入时,我就卡在骨架画面和内文之间的衔接;十月调星空效能时,我的目标是让进来的人觉得「怎么可以 3D 还顺成这样」。所以翻新第一天的 demo 我特别难接受:换页的飞行又干又单调,进场和退场是同一个方向,隔天改出来的版本也只是用淡入淡出把两页硬凑成连贯。
后来我给自己定了一条规则:每次换页都要从一个点延伸出去。首页是轨道上的卫星,碎念的彗核,手记的每道星轨也有一颗星。只要每一页都有自己的那个点,换页就能变成一颗点从这一页飞到下一页。
第一版像硬画的#
第一版很阳春:点沿着一条固定的路径飞,飞到之后新的一页淡入。实际看起来点跟页面上的线对不起来,琥珀点没有对齐彗核,转场画的线也跟彗尾差了一点。
改了几轮之后,「进作品页」那一段刚好做得最顺,真的是一气呵成。
我拿它当标准,其他页都照它改。把顺的原因拆开来看,有三件事:
首页的土星收进被点的那颗卫星,碎念的彗尾缩回彗核,页面上的东西都往那一点收拢,而且收完才换页。
这是我最在意的一点。镜头要先移到琥珀点上,把它完全置中;点出发后镜头跟拍,点一直在画面正中间;快到的时候镜头停下来,此时点还在动,才拍得到它身后的轨迹。就像电影先放大追踪一个对象,镜头停下时才看到它走过的路。
点最后顺着下一页本来就有的线(星轨、彗尾、航线)落下。点身后的彗尾会随时间散掉,所以落地时画面上留下的只有那一页自己的线。
每一页接住它的方式#
这三件事定下来之后,每一页要做的就是回答两个问题:点最后沿着哪条线落进来,离开时这一页怎么收回一个点。这也是我觉得最好玩的地方,同一颗点,在每一页都有不同的意思。
| 页面 | 点怎么落进来 | 离开时 |
|---|---|---|
| 首页 | 飞到土星中心,土星从那一点撑开 | 整组收进被点的那颗卫星 |
| 手记 | 切进一道星轨,绕着天极走一大段,停在那篇文章的星上 | 点进文章时,那篇的星轨被拉成文章的目录 |
| 碎念 | 顺着彗尾落成彗核 | 彗尾缩回彗核 |
| 作品 | 落在起点,当笔尖画出整条航线 | 航线从起点擦掉,收进最新那件的卫星 |
| 收藏 | 从深空俯冲回地球 | 镜头收回地球,点从台湾带着一圈波前发射 |
| 照片 | 点就是那道讯号,从探测器落进天线 | 整页收回 0 和 1,点从探测器发射 |
| 留言 | 起点振一下,一道琥珀光沿频道跑进天线 | 光从天线倒着跑回起点 |
| 朋友 | 落在中心,轨道从内往外一条条长出来 | 轨道收回中心 |
| 航行日志 | 落在发射台,火箭升空 | 火箭沿机身飞走,点跟着机鼻 |
| 关于 | 从正上方慢慢掉进句号 | 带着往下掉的速度离开 |
| 此站点 | 落在望远镜的镜筒前端,视线拉到第一颗星 | 从正在测的那颗星飞走 |
访客不一定是从首页进来的,所以开场也是同一套:第一次进站时是一片夜色,中间画着一圈,等页面载完,那一圈画满,中间那颗点就从画面正中间飞向这一页。
下定决心之后,这四天我没有想停下来过。现在我最喜欢的是照片、收藏、手记这几页,尤其是从手记的星轨接进文章内页那一段。虽然还有一点小卡顿,快速连续换页的手感也还没调到满意,但它们是目前最接近我心中「丝滑」的样子。
还没做好的#
写到这里看起来好像都很顺,但有几块我自己很清楚还没做好。
手机和 iPhone Duo#
整套设计是在宽萤幕上想的,手机只是「能看」而已。首页的 3D 土星在手机上缩得很小,卫星的名字要靠滑过才浮出,手机没有滑过这回事;作品页的螺旋在手机上转成直的,作品名挤在一起。今年 9 月出的 iPhone Duo 更麻烦,它展开之后内萤幕 7.6 吋、比例接近方形,刚好落在手机和桌面之间,现在的版面两边都不像。
这部分我还没有答案。很多页面的画面本身就是内容,不是把卡片排成一栏就能解决,大概要每一页重新想一次「在小萤幕上,这个天体应该怎么被看」。
可读性#
上线之后,X 上有人留言说可读性有点差,文字不好读、也不太知道该往哪里点。老实说刚设计出来的时候我也这样想过。导览列平常是收起来的,滑过才展开;首页一进来只有土星和几条线,没有任何一个按钮在告诉你「点这里」。
这是我刻意的选择。我希望进来的人第一时间感受到的是满版的震撼,还有在宇宙里自己摸索的感觉,但这两件事很难跟「好读、好找」取得平衡。如果我把字加粗、让文章内容多一点颜色,像以前的 MDX 区块那样花花绿绿,读起来确实会比较清楚,可是整站的风格就会开始不和谐。我还在一点一点调,但整体要调到哪里,我还没找到那条线。
被看见的那天#
上线隔天,我在后台看到一波从 X 来的流量,点进去才发现 innei 特别发了一篇推文介绍这个站,还在我的碎念底下留言。我一直很喜欢他的部落格,日式纸质风做得很美,文章里各种元素的互动、排版引擎都很细,也很佩服他的开发精神。我本来以为他大概不会看这种小站,所以那一刻真的觉得很不真实,开心又受宠若惊。不过开心完马上开始想:是不是哪里还要改、哪里做得不够好,害我又有点不想给人看了。
我参考过他的做法,现在他看到了我做出来的东西。这种感觉有点像《地。》里讲的传承:一个人留下的东西,被下一个人接过去,再带到更远的地方。
「如果说我看得比别人更远,那是因为我站在巨人的肩膀上。」
牛顿,1675 年写给虎克的信
也有站长是在 innei 的群组看到我的友链申请才找过来,说这个站做得很漂亮,顺便回报留言页送不出去,那个 bug 当天就修掉了。我一直觉得这个站是一个藏得有点深的宝藏,细节很多,但不一定有人会看到。这次翻新之后,开始有人从别的地方找过来,在里面点来点去,还会回来跟我说哪里不好读。
第 572 天#
翻新上线那天是这个站的第 572 天。航行日志页画的就是这段时间:往远方是时间,离地的高度是累积的 GitHub 贡献,到现在是 5,356 次。
回头看这条线,它一开始只是一页 Hero 加作品集。2025 年 8 月才有部落格,10 月有了自己的网域,今年 1 月整站搬到自家的 HomeLab。之后几乎每个月都在拆掉重来:四月做多语系、第一次大规模重做设计,六月把整站从 JavaScript 搬到 TypeScript、前端迁到 TanStack Start,七月把 Express 后端整包换成 Rust、让 WebGPU 的太空背景转正,然后是这次。WebGPU、Rust 后端、自架伺服器、五个语系的翻译,很多东西我都是因为这个站才第一次碰,也大多是踩了坑才学会,那些过程多半写成了手记。
旧站我没有删,冻结在 archive.koimsurai.com。它虽然不再是我要的样子,但新站能这么顺、这四天能做得这么快,很大一部分是靠它这一年多打下的架构。
这 572 天里最辛苦的其实不是写程式,是想文章题材的时候。我从来没想过放弃这个站,但常常会想:我为什么要做这些?好像一直都只有我自己在做。所以这次有人从别的地方找过来、在里面点来点去,对我来说比什么都实在。
这次翻新,比较像是把这 572 天学到的东西,第一次用我自己的美学整理成一个完整的样子。它还不完美,手机版、可读性、效能都还有一堆要改,但这条线还会继续往上爬。
附录:给想自己做的人#
下面是转场、字型和旧站的实作细节,跟上面的故事无关,有兴趣再看。
收、飞、长#
转场拆成三段:收、飞、长。换页先被挡下来,等旧页收完才放行;飞的时候新页已经挂上、跟着镜头从点要去的那一侧滑进来;点落地之后新页才开始长出自己的东西。
转场层(TransitionLayer)负责挡换页、画点和彗尾、移镜头;每一页只要用一个 hook 报上四件事:锚点在哪、点最后走哪条路、怎么收、落地时要做什么。这是关于页的版本,点从正上方直直掉进「坐稳了。」的句号:
usePageTransition({
// 点飞进来,停成标题的句号
anchor: () => stopPoint(titleStopRef.current),
// 最后一段:从句号正上方直直往下掉,镜头跟着往下,页面从下面升上来
arrive: () => {
const p = stopPoint(titleStopRef.current);
if (!p) return null;
const drop: Pt[] = [];
for (let d = FALL_IN_PX; d >= 0; d -= 6) drop.push({ x: p.x, y: p.y - d });
return { approach: drop, join: Math.round((FALL_IN_PX - FALL_SHOWN_PX) / 6), pace: FALL_PACE };
},
// 收:字退掉,琥珀点带着往下掉的速度离开
leave: () => {
const point = dotRef.current;
if (!point) return null;
pageRef.current?.classList.add('is-leaving');
return { point, velocity: { x: 0, y: 0.8 }, done: after(300) };
},
});approach 是点最后要走的那段路,join 标出镜头在哪一点煞停,前面那段可以在画面外。pace 小于 1 是放慢,关于页是自由落体,我让它掉得比别页慢。页面没给 leave 的话,转场层会用一圈圆形遮罩把整页收进锚点,所以新页面不用一开始就把转场写完。手记进文章时,那道星轨会交给转场层继续画着,等文章页长出来才淡掉,两页之间那条线不会断。挡换页用的是 TanStack Router 的 router.history.block,上一页、下一页(popstate)也会经过同一个 blocker,所以滑鼠侧键换页一样有转场。
边界情况#
飞行本身做顺之后,花最多时间的是各种边界情况。最明显的是快速连续换页:点画到一半的线加上悬停的点,会残留在下一页上。
- 快速连续换页:转场中又换页,旧的那趟直接收尾,这一次不演。页面的「收」最多等 0.8 秒,一趟转场超过 8 秒就当作出事、直接收掉。
- 背景星空每次换页会跳回原位,看起来像整片天空位移。改成镜头的位移累积下来,不归零。
- 用滑鼠侧键换页会退回预设的飞法。原因是页面判断「要往哪收」时看的是被点的那个元素,侧键换页时没有被点的东西。改成照目的地
ctx.to判断。 - 从一篇文章换到另一篇不演转场。文章页是原地换:左栏留着,只有标题、内文和目录换掉。
- 任何光晕、亮线都要渐退。瞬间暗掉看起来像断电,全站统一慢慢淡回页面原本的亮度。
- 开场:网路慢的话最多等 3.5 秒就飞,不会让人卡在载入画面。只换语系、进出后台,或系统设定了「减少动态」的读者,不会看到转场。
一页只载需要的字#
繁中思源宋体的可变字重版本一支 16.8 MB,简中、日文、韩文各一支,文章的字体切换另外还有黑体、霞鹜文楷、MiSans 几种,不可能整支丢给读者。我的做法是按页裁字:浏览器用 MutationObserver 收集画面上用到哪些字,跟伺服器要 /fonts/<风格>/<字重>/<键>.woff2?c=<字集>&f=<语系>,伺服器用 subset-font(底层是 HarfBuzz 的 wasm)裁出只含这些字的 woff2。网址由字集决定,所以可以永久快取。拿上一篇 FurSpark 的内文实测,全文用到 836 个不同的字,从 16.8 MB 的原档裁出 400 字重的 woff2 是 185 KB。
伺服器端裁字的核心程式
import subsetFont from 'subset-font';
// subset-font 执行期才用 require.resolve 找 HarfBuzz 的 wasm,打包工具追不到,
// 明确 import 一次,harfbuzzjs 整包(含 wasm)才会被带进输出
import { Blob as HbBlob, Face as HbFace } from 'harfbuzzjs';
// 裁一次要把整个原档复制进 wasm,一篇长文约 0.7 秒:一次只裁一个,排队
let queue: Promise<unknown> = Promise.resolve();
const serial = <T>(job: () => Promise<T>): Promise<T> => {
const run = queue.then(job, job);
queue = run.catch(() => undefined);
return run;
};
async function build(file: string, weight: 400 | 600, cps: number[]) {
const font = await sourceOf(file); // 原档只留最近用过的几支在记忆体
return subsetFont(font, String.fromCodePoint(...cps), {
targetFormat: 'woff2',
variationAxes: { wght: weight }, // 可变字重的原档,取出要的那个字重
});
}有两个坑值得记一下。第一个是打包:subset-font 在执行期才用 require.resolve 找 HarfBuzz 的 .wasm,Nitro 的追踪工具看不出这是一个相依,部署后才发现找不到档案,解法是把它设成外部相依(traceDeps),再明确 import 一次 harfbuzzjs。第二个是缺字:繁中页面上偶尔会出现简体字(通常是留言),现在请求可以多带一个 x=sc,伺服器先查繁中原档缺哪些字,只把缺的那些从简中那支补裁出来。
旧站怎么留#
上线前我把旧版冻结成 git tag v1-final 和一个 Docker 映像,另外开一个容器挂在 archive.koimsurai.com,跟新站共用同一个后端。
共用后端的风险是旧站的表单还能写资料,所以我在 nginx 那层把它锁成唯读:登入直接回空、API 只放读取、后台导回主站,也不让搜寻引擎收录。
旧站的 nginx 设定
server {
server_name archive.koimsurai.com;
add_header X-Robots-Tag "noindex, nofollow" always;
# 不让搜寻引擎收旧站,免得跟新站抢同一篇文章
location = /robots.txt {
default_type text/plain;
return 200 "User-agent: *\nDisallow: /\n";
}
# 后台导回主站
location /admin { return 302 https://koimsurai.com/admin; }
# 登入直接回空
location ^~ /api/auth/ { return 204; }
# API 只放读取
location /api/ {
if ($request_method !~ ^(GET|HEAD)$) { return 204; }
proxy_pass http://127.0.0.1:<后端埠>;
}
location / {
proxy_pass http://127.0.0.1:<旧站容器埠>;
}
}效能与上线后#
效能修正的原则是画面不能变。手记点进文章原本会顿一下,因为整篇内文在同一帧挂上,改成站内换页进来时分批挂;收藏页重整时的顿,是背景星空的 worker 正在建 WebGPU device,改成等进场动画跑完、主执行绪闲下来再建。
还没解决的是收藏页最外圈:讯号圈的虚线在卷到最外面时,每一帧要重切四到六万段,会从 65 fps 掉到 45 fps 左右。设备页的网路图和遥测也有一条原则:留结构、拿掉敏感型号,遥测只给温度、负载、记忆体、功耗,数字取整、快取 30 秒。上线后两天我又发了二十几个 PR,补迁移时漏掉的右键选单、搜寻面板、灯箱、电子报和后台,每一页的分享图也在后端用 Rust 的 tiny-skia 重画一次,输出成 APNG。
写到这里,这个站还在继续长。翻新不是终点,只是那条线上的又一个节点。最后,还是那句我最喜欢的话:
Stay hungry, stay foolish.
- subset-fontGitHub
- harfbuzzjsGitHub
- TanStack Router:Navigation Blockingtanstack.com
- NASA:Deep Space Networknasa.gov
- NASA:Pale Blue Dotscience.nasa.gov
- Apple:iPhone Duo 发表apple.com
- Instrument SerifGoogle Fonts
- Noto Serif TCGoogle Fonts