ZhihaiHub 博客主题开发记
引言
大家好,今天想跟你们聊聊我最近在折腾的一个博客主题——ZhihaiHub。说实话,做这个主题纯属一时兴起。之前用现成的主题总觉得差点意思,不是样式太花哨就是功能不够用。于是干脆自己动手,丰衣足食,花了两周多时间,总算搞出了一个还算满意的主题。今天就把整个开发过程整理一下,希望能给有同样想法的朋友一些参考。
为什么选择 Hexo + 自研主题
一开始也考虑过 Jekyll、Hugo 这些静态站点生成器,但最终还是选了 Hexo。原因很简单,生态成熟,插件丰富,而且中文文档多,遇到问题容易找到解决方案。
关于主题,市面上已经有太多优秀的第三方主题了,比如 Next、Butterfly 这些,功能确实强大。但我总觉得用别人的主题少了点什么——就像住精装房和自建房的区别,虽然后者麻烦,但住起来更顺手。
我的需求其实挺简单的:
- 加载要快,别整那些花里胡哨的动画
- 夜间模式必须有,现在谁还不用深色主题啊
- 代码高亮要好,毕竟经常发技术文
- 移动端体验要舒服,现在流量主要来自手机
明确了需求,接下来就是动手干。
技术栈与架构设计
主题的技术栈挺常规的:EJS 模板引擎 + SCSS + 原生 JavaScript。没有用 Vue 或 React,不是不会,而是觉得对于博客主题来说有点杀鸡用牛刀了。静态站点嘛,越简单越好维护。
目录结构
我参考了官方主题的规范,整理了这样一个结构:
zhihaihub/
├── layout/ # 模板文件
│ ├── index.ejs
│ ├── post.ejs
│ ├── page.ejs
│ └── _partial/ # 公共组件
├── source/
│ ├── css/
│ ├── js/
│ └── images/
├── _config.yml # 主题配置
└── package.json
这里有个小建议,做主题一定要把配置项抽离出来。用户安装主题后,最关心的就是能不能自定义颜色、头像、社交链接这些。把这些做成配置项,用户体验会好很多。
样式方案
CSS 预处理我选了 SCSS,比 Less 更灵活一些。变量、嵌套、Mixin 这些功能用起来确实方便。不过我尽量克制自己别写太复杂的嵌套,层级多了自己都看不下去。
// 变量定义
$primary-color: #3b82f6;
$text-color: #374151;
$bg-color: #ffffff;
$code-bg: #f6f8fa;
// 深色模式变量
$dark-text-color: #e5e7eb;
$dark-bg-color: #1f2937;
// 主题色切换
[data-theme="dark"] {
--text-color: $dark-text-color;
--bg-color: $dark-bg-color;
}
用 CSS 变量配合深色模式实现,这个方案兼容性很好,不需要 JavaScript 参与太多。
几个关键功能的实现
1. 代码高亮
技术博客,代码高亮是刚需。最开始想用 Prism.js,简单是简单,但总感觉差点意思。后来换成了 Highlight.js,主题选择更多,而且支持行号显示。
不过Highlight.js 默认会把整个代码块渲染出来,有时候会影响首屏加载。我的解决方案是按需加载,只在文章页面引入,首页和列表页不加载。这一个小优化,首页加载时间能快个几百毫秒。
// 代码高亮初始化
if (document.querySelector('pre code')) {
hljs.highlightAll();
}
2. 夜间模式
这个功能看似简单,其实细节挺多的。首先是配色,不能简单地把所有颜色反色,那样很难看。我参考了几个主流主题的配色方案,调了一个比较舒服的深色主题。
其次是状态保存。用户切换到深色模式后,刷新页面应该保持这个状态。这里用 localStorage 就够了:
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
toggleBtn.addEventListener('click', () => {
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
});
3. 响应式图片
博客文章里经常要放截图,图片一多加载就慢。我的做法是用 loading="lazy" 延迟加载,再配合 srcset 做响应式图片。不过 Hexo 本身对图片处理能力有限,这里主要靠文章作者手动指定不同尺寸的图片路径。
懒加载还有个好处是提升 SEO。Google Core Web Vitals 里的 LCP 和 CLS 这两个指标,对图片加载优化很敏感。优化到位了,搜索排名也会好看些。
4. 评论系统
评论功能我接入了 Giscus,基于 GitHub Discussions 实现。好处是免费、不用自己维护数据库,而且和 GitHub 生态完美集成。缺点是需要读者有 GitHub 账号才能评论,不过对于技术博客来说,这个门槛不算高。
性能优化做了这些
博客主题,性能是硬指标。我用 Lighthouse 反复测试,最终这几个指标都达到了 90 以上:
首屏渲染优化- CSS 内联关键样式,异步加载非关键 CSS
- JavaScript 放到页面底部 defer 加载
- 字体使用系统字体栈,避免加载延迟
资源加载优化- 图片统一用 WebP 格式,体积比 PNG 小 30% 以上
- 开启 Gzip 压缩(这个在服务器层面配置)
- 静态资源加缓存头
<!-- 异步加载 CSS -->
<link rel="stylesheet" href="<%- theme.css %>" media="print" onload="this.media='all'">
- 语义化 HTML,header、main、footer 标签正确使用
- alt 属性给所有图片安排上
- 合理的标题层级,h1 到 h6 顺序不能乱
踩过的坑
开发过程中没少踩坑,这里挑几个典型的说说:
第一个坑是 Hexo 的分页。 官方分页组件功能太少,想自定义样式特别麻烦。后来自己写了个简单的分页逻辑,用 JavaScript 控制显示多少页、当前页高亮这些。 第二个坑是 SEO。 刚开始没注意 meta 标签的设置,文章页面的 title、description 都是乱的。花了点时间把 Open Graph 和 Twitter Card 的标签都加上了,现在分享到社交平台能正常显示卡片。 第三个坑是 RSS。 忘记在主题里包含 RSS 插件的配置,导致有些读者说订阅不了。加上<%- feed_tag() %> 就解决了。
总结
花了大约两周的业余时间,ZhihaiHub 总算能见人了。虽然功能不算多,但基本满足了自己写博客的需求。用自己做的主题发文章,感觉确实不一样——就像开自己组装的电脑,那种成就感是买整机给不了的。
如果你也在用 Hexo,不妨试试自己动手改主题。不需要多厉害的技术,边做边学,遇到问题 Google 一下,几天就能搞出点名堂。过程比结果有意思多了。
好了,这篇分享就到这儿。如果有问题,欢迎在评论区留言交流。下次再跟你们聊聊这个主题的后续迭代计划,拜拜!
ZhihaiHub 博客主题开发记
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法