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 一下,几天就能搞出点名堂。过程比结果有意思多了。

好了,这篇分享就到这儿。如果有问题,欢迎在评论区留言交流。下次再跟你们聊聊这个主题的后续迭代计划,拜拜!