关于优化2.0

写在前面

我也没想到,这篇优化日志居然写到了2.0。与其说是日志,不如说是一堆小修小补的折腾记录——把遇到问题过程中的优化思路和踩过的坑整理归档,方便日后回查总结,也希望能给同样航向的船长一点参考。

本文档仅记录近期自己对网站的美化与优化,也可能是看似无意义的小踩坑。
近期收录的都是我问题的踩坑以及我的解决思路,你可以在本文 get:

  1. Service Worker 404 报错修复
  2. Live2D 看板娘懒加载与首屏提速
  3. Google Fonts 本地化与子集化
  4. 第三方资源本地化
  5. 自定义 CSS 瘦身
  6. COS 图片压缩与 PicGo 图床配置
  7. 反向推理与应急回退指南

阅读提示:本文档仅为个人的踩坑记录。深知自己代码功底尚浅,文中难免存在逻辑漏洞或不够优雅的写法。如果您发现任何错误,或有更好的优化方案,恳请各位大佬不吝赐教,感激不尽!


目录索引

  1. Service Worker 404 报错修复
  2. Live2D 看板娘首屏阻塞排查与延迟加载实践
  3. Google Fonts 本地化与子集化
  4. 第三方资源本地化
  5. 自定义 CSS 瘦身
  6. COS 图片压缩与 PicGo 图床配置
  7. 反向推理与应急回退指南

1. Service Worker 404 报错修复

1.1 问题记录

现象: 生成本地预览时在 Network 面板中,/sw.js 请求返回 404 Not Found

控制台报错:

Uncaught (in promise) Error: A bad HTTP response code (404) was received when fetching the script.

问题分析:

  • 排查出即使不需要 PWA 功能,这段代码也会每次都执行,白白发一次请求。
  • 在开发模式下(勾选了 Update on reload),controllerchange 事件触发后会导致页面陷入无限刷新的死循环。
  • 直接访问 GLOBAL_CONFIG 可能导致未定义报错。

1.2 排查记录

通过排查 _config.butterfly.ymlinject.bottom 配置,发现手动注入了一段 Service Worker 的注册代码。该代码在每次页面加载时都会强制请求 /sw.js,可我在博客根目录实际并未部署该文件,导致 404。

排查过程中发现以下三个坑:

  • 坑1:缺少容错处理 — 原代码没有 .catch(),找不到文件时浏览器直接抛出红色的 Uncaught (in promise) 错误。
  • 坑2:缺少防无限刷新机制 — 在开发模式下(勾选了 Update on reload),controllerchange 事件触发后会导致页面陷入无限刷新的死循环。
  • 坑3:全局变量直接访问 — 直接访问 GLOBAL_CONFIG 可能导致未定义报错。

1.3 解决思路

navigator.serviceWorker.register() 方法增加 .catch() 容错处理,当文件不存在时,静默处理错误,不再向控制台抛出红色异常。同时引入 refreshing 标志位拦截重复的 controllerchange 事件,防止无限刷新。使用 IIFE(立即执行函数)包裹代码,避免污染全局命名空间。

1.4 优化后代码

<script>
(function () {
// 1. 显示更新提示的函数
function showNotification() {
if (window.GLOBAL_CONFIG && GLOBAL_CONFIG.Snackbar) {
var isLight = "light" === document.documentElement.getAttribute("data-theme");
var bgColor = isLight ? GLOBAL_CONFIG.Snackbar.bgLight : GLOBAL_CONFIG.Snackbar.bgDark;
Snackbar.show({
text: "已更新最新版本",
backgroundColor: bgColor,
duration: 5e5,
pos: GLOBAL_CONFIG.Snackbar.position,
actionText: "點擊刷新",
actionTextColor: "#fff",
onActionClick: function () { location.reload(); }
});
} else {
// 备用方案:如果没有 Snackbar,直接显示自定义的 DOM 提示
var el = document.getElementById("app-refresh");
if (el) {
var bg = "light" === document.documentElement.getAttribute("data-theme") ? "#49b1f5" : "#1f1f1f";
el.style.cssText = "top: 0; background: " + bg + ";";
}
}
}

// 2. 注册 Service Worker
if ("serviceWorker" in navigator) {
// 防止无限刷新的标志位(非常关键!)
var refreshing = false;

window.addEventListener("load", function () {
navigator.serviceWorker
.register("/sw.js")
.then(function (registration) {
console.log("SW 注册成功,作用域:", registration.scope);

// 监听控制器变更(当有新版本激活时触发)
navigator.serviceWorker.addEventListener("controllerchange", function () {
// 如果正在刷新,直接返回,防止无限循环
if (refreshing) return;
refreshing = true;
showNotification();
});
})
.catch(function (error) {
// 3. 静默处理错误:如果 sw.js 不存在,不会在控制台报红
console.log("SW 注册跳过:", error.message || error);
});
});
}
})();
</script>

1.5 踩坑总结

踩坑点原因解决方案
控制台报红 404没有 .catch() 捕获错误添加 .catch() 静默处理
开发模式无限刷新controllerchange 事件重复触发引入 refreshing 标志位
GLOBAL_CONFIG 未定义直接访问全局变量使用 window.GLOBAL_CONFIG 安全访问

2. Live2D 看板娘首屏阻塞排查与延迟加载实践

2.1 问题记录

2.1.1 现象描述

在对博客进行本地化部署预览后,通过浏览器开发者工具(F12 → Network 面板)进行性能分析时,发现页面首屏渲染存在明显的”一卡一卡”现象。

2.1.2 Network 面板异常

在 Network 面板的瀑布流(Waterfall)中,观察到以下异常:

  • 核心模型文件 hijiki.model.json 的加载状态长时间处于 (pending)
  • 该文件未能及时下载完成,导致浏览器的渲染线程被阻塞,后续的图片、字体等关键资源排队等待,引发视觉上的卡顿

2.1.3 存在问题

首屏加载卡顿直接导致:

  1. 加载异常:打开页面后,文字和图片加载缓慢,视觉体验不流畅
  2. Lighthouse 评分低:首屏渲染指标(FCP、LCP)不达标,影响 SEO 排名
  3. 资源竞争:Live2D 模型文件与首屏核心资源(HTML、CSS、关键图片)争夺有限的网络并发连接数

2.2 排查记录

2.2.1 排查思路

针对 (pending) 状态,我排查方向主要集中在以下两方面:

排查方向怀疑原因验证方式
网络层模型文件体积过大,或服务器(腾讯云 COS)响应过慢,导致下载超时检查 COS 控制台响应时间、文件大小
执行层Live2D 的初始化脚本在页面加载时同步执行,抢占了浏览器的网络并发连接数和主线程资源审查 Network 瀑布流、检查脚本加载方式

2.2.2 排查过程

  1. 第一步:审查 Network 瀑布流

    • 发现 hijiki.model.json 长时间处于 (pending) 状态
    • 该文件的依赖资源(.png 贴图、.moc 动作文件)也同步发起请求
  2. 第二步:排查服务器响应

    • 登录腾讯云 COS 控制台,检查对应存储桶的响应时间
    • 确认服务器响应正常,排除网络层问题
  3. 第三步:深入分析脚本加载方式

    • 审查 _config.butterfly.ymlinject 配置
    • 发现 Live2D 的初始化脚本 L2Dwidget.min.js 通过 <script> 标签同步加载,没有 deferasync 属性
  4. 第四步:确认根因

    • 同步加载的脚本会阻塞 DOM 解析,浏览器被迫分配资源去下载和解析看板娘模型
    • 在 HTTP/1.1 协议下,浏览器的并发连接数受限(通常每个域名最多 6 个),大体积模型文件迅速占满带宽

2.2.3 原因分析

经过排查,确认卡顿的根本原因是 Live2D 插件的同步加载机制与首屏渲染产生了资源竞争,具体问题是两个方面:

  1. 模型文件体积庞大:Live2D 看板娘不仅需要加载 .model.json 配置文件,还会触发大量贴图(.png)和动作文件(.moc)的并发请求。在 HTTP/1.1 协议下,浏览器的并发连接数受限,这些大体积文件会迅速占满带宽。

  2. 渲染阻塞:Live2D 的初始化脚本(如 L2Dwidget.min.js)通常在 DOM 解析时同步执行。在页面主内容(HTML、CSS、核心图片)尚未渲染完成时,浏览器被迫分配资源去下载和解析看板娘模型,直接导致了首屏加载的阻塞。

2.3 踩坑记录

坑1:初期误判为网络层问题

排查初期,我首先怀疑是腾讯云 COS 服务器响应过慢或模型文件体积过大导致的下载超时。在 COS 控制台逐一检查了响应时间和文件大小,结果服务器响应正常,排除了网络层问题。这一轮排查浪费了不少时间,说明遇到 (pending) 状态时,不能只盯着网络层看,执行层的同步加载同样会导致资源长时间 pending。

坑2:同步加载的隐蔽性

Live2D 的初始化脚本通过 <script> 标签同步加载,没有 deferasync 属性。这种同步加载方式会阻塞 DOM 解析,但浏览器不会报任何错误或警告,我的控制台干干净净,只有通过 Network 面板的瀑布流才能发现模型文件长时间 pending 的异常。如果只看控制台日志,很容易忽略这个问题。

坑3:延迟时机的选择

在实现延迟加载时,最初尝试使用 DOMContentLoaded 事件监听来触发看板娘的显示。但 DOMContentLoaded 事件在 DOM 解析完成后即触发,此时部分外部资源(如图片、CSS)可能尚未加载完毕,导致看板娘提前显示时页面尚未完全渲染,视觉上仍然不够流畅。最终改用 window.load 事件,确保所有资源(包括图片、样式表等)全部加载完成后再延迟显示看板娘,体验明显好转。

2.4 操作方法

2.4.1 核心策略

采用 “默认隐藏 + 页面加载完成后延迟显示” 的异步加载策略:

  • CSS 隐藏:在页面初始渲染时将 Live2D 容器强制隐藏(display: none),避免占用首屏渲染空间
  • JS 延迟:监听 window.onload 事件,确保页面所有核心资源加载完毕后,再通过定时器延迟初始化并显示看板娘

2.4.2 步骤一:编写 CSS 隐藏样式

在 Hexo 根目录的 source/css/ 下新建或修改 custom.css,添加以下样式:

/* 默认隐藏 Live2D 看板娘,避免阻塞首屏渲染 */
#live2d-widget {
display: none !important;
}

说明:使用 !important 确保覆盖主题默认样式,防止其他 CSS 规则将此元素设为 display: block

2.4.3 步骤二:编写 JS 延迟加载逻辑

在博客根目录的 source/js/ 下新建 delay-live2d.js,编写以下脚本:

window.addEventListener('load', function() {
// 延迟 2000毫秒(2秒)后显示看板娘,确保首屏资源优先加载
setTimeout(function() {
var widget = document.getElementById('live2d-widget');
if (widget) {
widget.style.display = 'block';
}
}, 2000);
});

说明

  • 使用 window.addEventListener('load', ...) 而非 DOMContentLoaded,确保所有资源(图片、CSS)加载完成后再执行
  • setTimeout 延迟 2000ms(2秒),给浏览器足够的时间完成首屏渲染
  • 通过 document.getElementById('live2d-widget') 获取容器元素,仅修改 display 属性,不重新创建 DOM

2.4.4 步骤三:通过配置注入

在根目录的配置文件 _config.butterfly.yml 中,利用 inject 字段将上述文件注入到页面中:

inject:
head:
# 在头部引入 CSS,确保初始状态为隐藏
- <link rel="stylesheet" href="/css/custom.css">
bottom:
# 在页面底部引入 JS,确保不阻塞 DOM 解析
- <script src="/js/delay-live2d.js"></script>

说明

  • CSS 放在 inject.head 中,确保在页面渲染初期就生效,看板娘容器从一开始就是隐藏的
  • JS 放在 inject.bottom 中,确保 DOM 解析完成后再执行,避免阻塞页面渲染

2.4.5 步骤四:验证效果

hexo三连:

hexo clean && hexo g && hexo s

通过调整 Live2D 的加载时机,成功将非首屏关键资源从核心渲染路径中剥离。具体手段包括:

  1. CSS 隐藏:通过 display: none 在初始渲染阶段隐藏看板娘容器
  2. JS 延迟加载:通过 window.load + setTimeout 实现 2 秒延迟显示
  3. 主题注入配置:通过 Butterfly 的 inject 机制将 CSS 和 JS 分别注入到 <head> 和页面底部

这次恰巧印证了我的一个前端性能优化原则:对于不影响首屏内容的第三方组件,应尽量采用延迟加载(Lazy Load)或异步执行(Async/Defer)策略。

2.5 踩坑总结

踩坑点原因解决方案
初期误判网络层只检查 COS 响应时间和文件大小同时排查执行层的同步加载
同步加载隐蔽性强浏览器不报任何错误通过 Network 瀑布流发现 pending
DOMContentLoaded 时机过早外部资源尚未加载完毕改用 window.load 事件

3. Google Fonts 本地化与子集化

3.1 问题记录

现象: F12检查发现 Network 面板显示 css?family=Titillium+Web&display=swap 请求耗时高达 700ms+。

页面表现: 因跨洋网络延迟或 DNS 解析问题,导致页面文字长时间不可见(FOIT,Font Visible Instantly Timeout),严重阻塞渲染。

3.2 排查记录

  1. 检查 hexo-google-fonts 插件配置,确认插件已安装但远程请求未切断。
  2. 查阅主题文档,发现主题本身也自带了 Google Fonts 的加载开关。
  3. 确认需要同时关闭插件和主题的远程字体加载,才能彻底切断跨洋请求。

3.3 解决思路

  • 彻底切断对 Google 远程服务器的请求,改用本地部署字体文件。
  • 安装 hexo-google-fonts 插件,在根目录 _config.yml 中配置所需字重和子集化(仅加载拉丁字符,极大减小体积)。
  • 在主题配置中关闭默认的远程加载开关(Google_fonts: false)。

3.4 核心配置

# 博客根目录 _config.yml
google_fonts:
enable: true
fonts:
- family: Titillium Web
weights: [400, 600, 700] # 仅下载常用字重
subsets: [latin] # 仅下载拉丁字符集
subset: true # 开启字符子集化

3.5 踩坑总结

踩坑点原因解决方案
字体加载慢请求跨洋 Google 服务器本地化部署字体文件
双重加载插件和主题同时加载远程字体同时关闭两者的远程开关
字体体积大下载了完整字符集开启 subset: true 仅下载拉丁字符

4. 第三方资源本地化

4.1 问题记录

现象: inject 配置中引用了 font-awesome-animation.min.css 的外部 CDN 链接。

页面表现: 国内加载不稳定,可能拖慢页面初始化速度。

4.2 排查记录

  1. 审查 inject.head 配置,发现引用了 jsDelivr CDN 的远程链接。
  2. 确认该 CDN 在国内部分地区访问缓慢或不稳定。
  3. 决定将资源下载到本地,替换远程引用路径。

4.3 解决思路

使用 npm 下载该库到本地 source/css/ 目录,并修改引用路径为本地路径。这样无论外部 CDN 是否可用,页面都能正常加载动画效果。

4.4 操作命令与配置

终端执行:

npm install font-awesome-animation --save
cp node_modules/font-awesome-animation/dist/font-awesome-animation.min.css source/css/

修改前:

# _config.butterfly.yml
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/l-lin/font-awesome-animation/dist/font-awesome-animation.min.css" media="defer" onload="this.media='all'">

修改后:

# _config.butterfly.yml
- <link rel="stylesheet" href="/css/font-awesome-animation.min.css" media="defer" onload="this.media='all'">

4.5 踩坑总结

踩坑点原因解决方案
CDN 国内不稳定服务器在海外资源本地化
删除远程链接后 404本地文件未复制先复制文件再改路径
media="defer" onload需确保本地文件路径正确用绝对路径 /css/xxx

5. 自定义 CSS 瘦身

5.1 问题记录

现象: 自定义 CSS 代码(yao.css)中包含已删除的时钟卡片样式(无效代码),且滚动条样式缺乏 Firefox 兼容性。

具体问题:

  • .card-clock 相关样式指向已删除的 DOM 元素。
  • #footer-wrap a.footer_custom_text 颜色值重复,可合并。
  • 滚动条仅使用了 -webkit-scrollbar 伪元素,Firefox 用户看不到自定义滚动条。

5.2 排查记录

  1. 审查 yao.css 文件,逐条检查样式选择器对应的 DOM 元素是否还存在。
  2. 发现 .card-clock 的 HTML 元素已在之前的优化中被删除,但 CSS 样式未同步清理。
  3. 检查滚动条样式,发现只写了 WebKit 内核的伪元素,缺少标准 CSS 属性。

5.3 解决思路

  • 删除无效的 .card-clock 样式,减少无意义的文件体积。
  • 合并相同颜色的选择器(#footer-wrap a.footer_custom_text),精简代码。
  • 补充标准的 scrollbar-widthscrollbar-color 属性以兼容 Firefox。

5.4 优化后 CSS

/* 作者名颜色 */
.author-info__name { color: #ff7242; }

/* 页脚链接颜色(合并优化) */
#footer-wrap a, .footer_custom_text { color: #00c4b6; }

/* 网页滚动条样式 */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb {
background-color: #e58a8a;
background-image: -webkit-linear-gradient(45deg, rgba(255,255,255,.4) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.4) 50%, rgba(255,255,255,.4) 75%, transparent 75%, transparent);
border-radius: 2em;
}

/* Firefox 滚动条兼容 */
* {
scrollbar-width: thin;
scrollbar-color: #e58a8a transparent;
}

5.5 踩坑总结

踩坑点原因解决方案
无效 CSS 堆积删除 HTML 后忘记清理 CSS定期审查 unused CSS
Firefox 滚动条不显示只写了 WebKit 伪元素补充标准 scrollbar-width 属性
选择器重复不同选择器设了相同颜色用逗号合并选择器

6. COS 图片压缩与 PicGo 图床配置

6.1 问题记录

现象: Network 面板显示首屏存在一张 6.5MB 的 JPG 图片,这是目前拖慢网站速度的最大元凶。

隐患: 作为网站图床,图片被其他网站盗链会导致流量费爆炸。

6.2 排查记录

  1. 检查腾讯云 COS 控制台,确认存储桶的访问权限、数据处理规则及防盗链配置。
  2. 检查 PicGo 插件配置,确认上传路径与自定义域名是否匹配。

6.3 踩坑记录

  • 坑1:缓存未刷新 — 修改防盗链或压缩规则后,CDN 节点上可能还缓存着旧的大图,导致优化不生效。
  • 坑2:压缩过度 — WebP 质量设太低,手机端图片模糊。

6.4 解决思路

  • 无感压缩:开启腾讯云数据万象的”极智压缩”自动处理,URL 保持不变。
  • 安全防线:PicGo 存储桶设置为”公有读私有写”,绑定自定义域名并开启 CDN 强制 HTTPS,配置 Referer 白名单防盗链。

6.5 操作归档

COS 极智压缩验证

# 访问图片时,检查响应头
X-Slimflag: 1 # 1 表示压缩成功,0 表示失败,2 表示未压缩

PicGo 图床配置速查

设置项推荐值原因
访问权限公有读私有写网站访客必须能看图,只有你能传图
域名自定义域名 + CDN默认域名太慢且丑,CDN 加速全国访问
协议强制 HTTPS适配现代浏览器安全策略
图片处理开启极智压缩实现”原链接访问,后台自动瘦身”
安全防护开启防盗链防止别人偷用你的图,导致你破产

7. 反向推理与应急回退指南

当服务器崩了或优化过度时,请按此逻辑路径自救

关于自救,这是我多年排查经验的方法

7.1 六步排查法

  1. 看日志hexo s --debug 查看具体报错行号。
  2. 查变更:回顾最近一次 git log 或文件修改时间。
  3. 停插件:注释掉 _config.yml 中所有新加插件,恢复默认。
  4. 清缓存hexo clean && rm -rf node_modules && npm install
  5. 验环境node -v pnpm -v 确认版本未自动升级。
  6. 读文档:回到本文档对应章节,确认是否遗漏配置项。

7.2 回退命令速查

# 紧急停止服务
Ctrl + C

# 清理生成文件与缓存
hexo clean

# 卸载可疑插件
npm uninstall <plugin-name>

# 重置依赖树 (慎用)
rm -rf node_modules package-lock.json
npm install

# 恢复配置文件 (如有备份)
git checkout _config.yml

7.3 优化操作清单回顾表

优化项风险等级预期收益回退方案
SW 容错处理消除控制台报错删除 .catch() 逻辑
Live2D 延迟加载LCP 提升 1s+移除 setTimeout 脚本
字体本地化消除 FOIT,首屏提速开启 font-display:swap
第三方资源本地化消除 CDN 不稳定因素还原远程 CDN 链接
CSS 瘦身减少文件体积还原旧版 yao.css
COS 极智压缩流量费减半关闭数据处理

结语
折腾的意义不在于完美,而在于掌控。每一次报错都是系统在与你对齐认知。愿这份日志成为你数字花园里的”路标”,无论走多远,都能循迹而归。