背景视频 6.4 MB → 1.6 MB · 看板娘贴图 1.1 MB → 0.24 MB · 控制台里的红字少了一大片
但有一件事,压缩解决不了。
起点:搬上服务器,然后发现它很慢
上一篇把博客搬上了服务器,24 小时在线了。
然后打开首页:背景视频半天不动,音乐点了没声音,右下角的看板娘干脆没出来。打开控制台,一片红。
这一篇记录这晚的排查。
第零个坑:明明发了文章,首页却没有
上一篇发出去之后,我自己的浏览器里首页还是旧的:归档 5 篇、项目 3 篇,新文章不见了。
先一层层排除:
| 检查 | 结果 |
|---|---|
| 服务器上的文件 | 新版 |
| Cloudflare 缓存 | 没缓存,每次回源 |
| 直接请求首页 | 新版 |
| 代理 / Service Worker | 都没有 |
只剩浏览器自己。服务器之前没告诉浏览器「网页要每次重新确认」,浏览器就心安理得地用着旧页面。
修法是给 HTML 加一行响应头:
location ~* \.html?$ { add_header Cache-Control "no-cache" always; }no-cache 不是「不缓存」,而是「用之前先问一句有没有更新」。图片、样式照常长期缓存,只有网页本身每次确认。
第一步:先量,再改
把首页上几个大文件挨个测了一遍下载时间:
| 文件 | 大小 | 下载时间 |
|---|---|---|
| 背景视频 | 6.4 MB | 60 秒还没下完 |
| 歌曲 | 2.7 MB | 34 秒 |
| 看板娘贴图 | 1.1 MB | 9–16 秒 |
| 看板娘渲染脚本 | 644 KB | 5 秒 |
速度只有每秒几十到一百多 KB。文件又大,叠在一起就是「半天不动」。
第二步:能压的都压一遍
背景视频:6.4 MB → 1.6 MB
这段视频有几个特点:
- 画面上本来就加了 6px 模糊,再清晰也看不出来
- 它是静音播放的,但文件里带着一条音轨
- 640×360、30 帧
所以:去掉音轨,降到 480 宽、24 帧,提高压缩率。
ffmpeg -i bg.mp4 -an -vf "scale=480:-2,fps=24" \ -c:v libx264 -preset slow -crf 33 -movflags +faststart bg-new.mp4另外两处小改动:
- 加了一张 10 KB 的封面图,视频没加载出来之前先显示第一帧,不会黑屏
preload="auto"改成preload="metadata",打开页面时不再急着把整个视频拉下来
音乐:128k → 96k
两首歌分别从 2.7 MB、2.2 MB 降到 2.0 MB、1.7 MB。背景音乐,96k 够用。
顺手发现目录里还躺着两个 .mp3.orig,是之前压缩前的原文件,一共 12 MB,没有任何地方引用,却每次都跟着博客一起发布。移走。
看板娘贴图:4096 → 2048
贴图是 4096×4096 的 PNG。而看板娘在页面上只有 280 像素宽。
缩到 2048×2048,1.1 MB → 0.24 MB。
还试了更狠的 pngquant 有损压缩,能压到 72 KB,但逐像素比对后,部分边缘像素的差异到了最大值 255 —— 也就是有像素颜色完全变了。贴图上一个像素对应模型的一小块,这种失真可能会在某个动作里露出来,不冒这个险,用了无损的 2048 版本。
压完之后
| 文件 | 之前 | 之后 |
|---|---|---|
| 背景视频 | 60 秒还没下完 | 13–24 秒 |
| 歌曲 | 34 秒 | 6–9 秒 |
| 看板娘贴图 | 9–16 秒 | 3–5 秒 |
快了不少。但还是不快。
第三步:压缩解决不了的那件事
注意看这一行:
看板娘渲染脚本,644 KB,已经被 Cloudflare 缓存(HIT),下载用了 3.8 秒。
文件已经存在 Cloudflare 的节点上,根本不用回我的服务器了,还是要 3.8 秒。
算下来,从我这边到 Cloudflare 只有 每秒 100–150 KB。免费版 Cloudflare 在国内要绕到海外节点,这条线路本身就慢。
所以结论很明确:
- 文件大小是我能改的,已经改了
- 线路是我改不了的,换服务器也没用
真想快,只能走另一条路(国内 CDN),而那需要先办备案。这是以后的事了。
第四步:看板娘为什么两次都加载失败
控制台里有两行黄字:
[pio] model not loaded in 25s, retry=false[pio] model not loaded in 25s, retry=true看板娘的加载页里有个「看门狗」:25 秒内没加载完,就当作失败,清掉重来一次;第二次还不行,就显示「加载失败」。
这个设计本来是为了应对显卡偶尔抽风的情况。但翻服务器日志,发现真正的原因是:
- Live2D 要依次下载十几个文件,每个小文件在我这边都要等 5–20 秒
- 第一次和第二次,最后那张贴图都还没开始下载,25 秒就到了
不是加载失败,是还没来得及加载就被判了失败。
改法:
| 改动 | 原因 |
|---|---|
| 超时 25 秒 → 90 秒 | 慢网络下给足下载时间 |
| 模型配置 JSON 缓存 1 天 | 原来每次都要回服务器确认一遍,十几个文件就是十几次往返 |
| 视频、音乐、模型文件缓存 30 天 | 原来是 4 小时 |
第一次打开可能还是要等一会儿,但之后文件都在浏览器里了。
第五步:控制台里那一片红
剩下的报错长这样,重复了很多遍:
GET https://api.unisvg.com/material-symbols.json?icons=searchnet::ERR_CONNECTION_TIMED_OUT博客里的按钮图标 —— 主题切换、搜索、音乐播放器 —— 用的是 Iconify 图标组件。图标找不到时,它会去在线服务器下载,服务器有三个:
| 服务器 | 从我这边访问 |
|---|---|
| api.iconify.design(官方) | 0.4 秒 |
| api.simplesvg.com(备用) | 0.4 秒 |
| api.unisvg.com(备用) | 连不上 |
奇怪的是:这些图标明明早就打包进博客了。项目里有一个专门的脚本,把用到的 149 个图标都提前注册好,就是为了不联网。
比对了一遍,149 个一个不缺。那为什么还要去下载?
原因:顺序反了
看构建出来的页面,脚本的执行顺序是:
搜索框组件 ← 用到图标,本地还没有,去联网下载主题切换组件 ← 同上设置面板 ← 同上……本地图标注册 ← 排在最后注册图标的脚本挂在页面底部,而那些组件先跑了。等图标注册好,请求早就发出去了。
修法:让组件自己先注册
与其和执行顺序较劲,不如让「用图标的组件」和「注册图标」变成同一件事。
写了一个很小的替身模块:
// 先注册本地图标,再导出原组件import "./iconify-local";import { addAPIProvider } from "@iconify/svelte/dist/functions";
// 兜底时只用官方 API,去掉连不上的备用镜像addAPIProvider("", { resources: ["https://api.iconify.design"] });
export { default } from "@iconify/svelte/dist/Icon.svelte";然后在构建配置里,把所有 @iconify/svelte 的引用都指向这个替身。这样不管哪个组件先加载,它拿到图标组件的那一刻,本地图标就已经注册好了。
顺便还补上了一个漏网之鱼:音乐播放器缓冲时转圈的图标 svg-spinners:90-ring-with-bg,之前没被打包进去。
最后剩下的
控制台里还有一行:
static.cloudflareinsights.com ... ERR_TIMED_OUT这个不是博客的代码,是 Cloudflare 自动往页面里插的访问统计脚本,国内连不上。要在 Cloudflare 后台关掉,跟博客无关。
收尾:两条黄色警告
红字清干净以后,控制台里还剩两条黄色的警告。不影响使用,但看着碍眼。
「点击进入」和读屏软件打架
Blocked aria-hidden on an element because its descendant retained focus.Element with focus: <button.ml-enter>进站页(就是那个流星 +「点击进入」的遮罩)从一开始就被标记成 aria-hidden="true" —— 意思是「读屏软件请忽略我」。可它里面偏偏有一个能获得焦点的按钮。
一边说「别看我」,一边又把焦点放在自己身上,浏览器就拦了。
改法很直接:
- 进站页显示的时候不再隐藏,它本来就是用户要操作的东西
- 关闭的时候先让按钮失去焦点,再标记隐藏
看板娘还没出来,就在找它的「手」
[pio] no driver found; widgetInstance keys = ...看板娘会跟着鼠标转头,这个功能要先在 Live2D 库里找到负责拖动的那个接口。问题是它在模型加载完之前就开始找了,当然找不到,于是报了一条警告 —— 几秒后模型加载完,它又找到了:
[pio] found onDrag at ... (depth 6)所以这条警告其实是误报。改成只在模型已经加载完、还找不到的时候才提示。
这一晚学到的
- 先量,再改。 不测一遍,我会以为慢是服务器的问题
- 能压的先压,但要知道压缩的上限在哪 —— 线路慢,文件再小也只能快一些
- 「失败」不一定是失败。 看门狗 25 秒判死刑,判的其实是网速
- 「已经做了」不等于「已经生效」。 图标早就打包了,但执行顺序让它形同虚设
- 有损压缩要验证。 72 KB 很诱人,但逐像素比一下,就知道不该用
- 警告也值得看一眼。 一条是两个设计互相矛盾,一条是时机不对的误报 —— 都不是大事,但都说明了代码在想什么
本文中的排查和修改都由 AI 执行,涉及权限的每一步都先征得了我的同意。改动前的文件都留了备份。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时