上一篇:本站自定义组件合集 · 系列总目录 · 下一篇:无

前言

随着自定义样式和组件不断增加,一个自然的问题是:网站是否应该彻底脱离 Butterfly,建立自己的主题?

答案不取决于修改了多少行 CSS,而取决于我们是否需要控制页面结构,以及是否愿意长期维护模板、兼容性和第三方集成。

本文不会直接替换现有主题,而是给出一条可以随时停止、不会影响线上网站的渐进路线。


一、配置文件不是主题本体

_config.butterfly.yml 只是控制面板。真正的 Butterfly 主题包括:

1
2
3
4
5
layout/      页面和组件模板
source/ 原生样式与脚本
scripts/ Hexo生成辅助逻辑
languages/ 多语言文字
_config.yml 默认配置

配置写着:

1
2
3
aside:
card_recent_post:
enable: true

并不会自动产生卡片。Butterfly 的模板读取这个值,遍历文章数据并生成 HTML,样式再负责排版。

因此独立主题不是复制一份配置,而是重新实现配置背后的行为。


二、当前网站仍然依赖 Butterfly 的部分

主要依赖包括:

  • 首页文章卡片模板
  • 文章页标题和正文结构
  • 导航栏与移动菜单
  • 归档、分类和标签模板
  • 侧栏卡片
  • 文章目录
  • 上一篇、下一篇和相关文章
  • 评论、分享、搜索入口
  • PJAX 页面替换
  • 明暗主题基础状态
  • Open Graph 和结构化数据

而更接近个人主题资产的部分包括:

  • 背景与明暗氛围
  • 音乐播放器
  • 分类磁贴
  • 标签星座
  • 友链卡片
  • 全局命令面板
  • 首页布局切换
  • 页脚内容
  • 封面图池
  • 更新日志自动化

这意味着网站已经拥有自己的“组件层”,但“页面骨架层”仍主要来自 Butterfly。


三、什么时候值得独立

适合继续基于 Butterfly:

  • 当前布局已经满足需要
  • 主要修改集中在颜色、背景和组件
  • 希望继续获得主题更新
  • 不想维护评论、搜索、SEO 等基础设施

适合开始独立主题:

  • 经常需要覆盖 Butterfly 的 DOM 结构
  • 自定义 CSS 中出现大量 !important
  • 多个脚本依赖脆弱的主题选择器
  • 希望重新设计首页和文章页的信息层级
  • 愿意维护移动端、可访问性和第三方服务

增加更多组件后再独立,不一定会显著更困难。只要组件彼此独立、没有直接修改 node_modules,它们仍然可以迁移。真正增加成本的是对 Butterfly 内部结构形成越来越多隐式依赖。


四、先定义自己的主题边界

独立主题第一版不需要复制 Butterfly 全部能力。可以先定义最小范围:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
必须:
首页
文章页
普通页面
分类
标签
归档
导航
页脚
明暗主题
移动端

第二阶段:
文章目录
搜索
评论
相关文章
图片灯箱

可选:
多评论系统
广告
在线聊天
多语言
PWA
大量装饰特效

不需要因为 Butterfly 预留了十种评论系统,自己的主题也预留十种。


五、设计新的目录

可以在现有博客中创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
themes/unmyic/
├── _config.yml
├── layout/
│ ├── index.ejs
│ ├── post.ejs
│ ├── page.ejs
│ ├── archive.ejs
│ └── partial/
│ ├── head.ejs
│ ├── header.ejs
│ ├── footer.ejs
│ ├── post-card.ejs
│ └── sidebar.ejs
├── source/
│ ├── css/
│ ├── js/
│ └── img/
├── scripts/
└── languages/

选择 Pug、EJS 或 Nunjucks 都可以,关键是项目只保留一种主要模板语言。对于第一次编写 Hexo 主题,EJS 语法比较直观;如果希望复用 Butterfly 的部分理解,也可以继续使用 Pug。


六、设计精简配置

与其复制 Butterfly 一千多行,可以先建立自己的配置模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
identity:
logo: /img/weblogo.svg
avatar: /img/avatar.png
description: 记录学习与创造

navigation:
- name: 首页
url: /
- name: 归档
url: /archives/

appearance:
default_mode: light
color: "#3B82F6"
background:
light_desktop: /img/background-light-desktop.webp
dark_desktop: /img/background-dark-desktop.webp
light_mobile: /img/background-light-mobile.webp
dark_mobile: /img/background-dark-mobile.webp

post:
toc: true
copyright: true
related: true

components:
music_player: true
command_palette: true
category_tiles: true

performance:
pjax: true
lazyload: true

配置只保留自己真正实现并愿意维护的能力。


七、建立模板的数据契约

主题模板主要读取 Hexo 提供的数据:

1
2
3
4
5
6
config        站点配置
theme 主题配置
page 当前页面或文章
site.posts 全部文章
site.tags 标签集合
site.categories 分类集合

文章卡片组件可以约定只依赖:

1
2
3
4
5
6
7
8
post.title
post.path
post.date
post.updated
post.description
post.cover
post.categories
post.tags

一旦契约清晰,随机封面图池只需继续向 post.cover 提供结果,主题无需了解封面是人工填写还是随机分配。


八、先实现最小页面

迁移顺序建议:

第一步:基础布局

实现:

1
2
3
4
head
header
main
footer

确保 CSS、JavaScript、favicon、标题和 meta 能正常输出。

第二步:首页和文章页

首页先只输出文章标题和链接:

1
2
3
4
5
6
7
8
9
<% page.posts.each(function (post) { %>
<article class="post-card">
<h2>
<a href="<%- url_for(post.path) %>">
<%- post.title %>
</a>
</h2>
</article>
<% }) %>

文章页输出:

1
2
3
4
5
6
<article class="post">
<h1><%- page.title %></h1>
<div class="post__content">
<%- page.content %>
</div>
</article>

先验证数据正确,再迁移复杂外观。

第三步:列表页面

依次实现:

  • 归档
  • 分类详情
  • 标签详情
  • 分类总览
  • 标签总览

第四步:个人组件

优先迁移与 Butterfly 耦合较低的组件:

  1. 背景与主题色
  2. 音乐播放器
  3. 页脚
  4. 命令面板
  5. 友链卡片

最后迁移依赖当前 DOM 的分类磁贴、文章布局切换和目录增强。


九、不要在现有网站上直接切换

独立主题应与线上主题并行开发。

建议:

1
2
3
正式目录:blog-demo
测试分支或副本:theme-development
独立主题:themes/unmyic

每完成一个阶段,都用同一批文章进行对比构建。至少检查:

  • 首页分页
  • 长标题和长摘要
  • 没有封面的文章
  • 包含 LaTeX 的文章
  • 大型代码块
  • 分类和标签
  • 手机端
  • 明暗主题
  • 404 页面

只有在最小功能全部通过后,才把:

1
theme: butterfly

切换为:

1
theme: unmyic

十、处理 PJAX 与组件生命周期

如果独立主题继续使用 PJAX,需要为所有组件定义统一生命周期:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
window.UnmyicTheme = {
components: [],

register(component) {
this.components.push(component);
},

init() {
this.components.forEach(function (component) {
component.init?.();
});
},

destroy() {
this.components.forEach(function (component) {
component.destroy?.();
});
}
};

页面替换前执行 destroy,替换后执行 init。这比每个文件自行监听多个事件更容易长期维护。

如果第一版不需要无刷新跳转,可以暂时不实现 PJAX。正常页面刷新虽然没有那么“应用化”,但行为简单、稳定,也更容易调试。


十一、保留升级和回退能力

迁移过程中应保留:

  • 当前可工作的 Butterfly 配置
  • package-lock.json
  • 所有自定义资源
  • 构建和部署脚本
  • 封面固定映射
  • 独立主题测试记录

不要删除 Butterfly,也不要覆盖现有主题文件。只有在独立主题稳定运行一段时间后,再决定是否移除依赖。

Git 提交可以按阶段组织:

1
2
3
4
5
feat(theme): scaffold unmyic theme
feat(theme): add index and post layouts
feat(theme): add archive taxonomy pages
feat(theme): migrate color and background system
feat(theme): migrate custom components

这样任何阶段出现问题都能精确回退。


十二、独立不等于从零发明

Hexo 提供数据和生成系统,第三方库提供公式、评论、图表和图片预览。独立主题的意义不是拒绝所有依赖,而是:

  • 页面结构由自己决定
  • 配置只保留真正需要的能力
  • 组件生命周期统一
  • 视觉语言保持一致
  • 升级边界清晰

Butterfly 仍然可以作为成熟主题设计和兼容处理的参考。若复制或改编其源码并发布,应阅读并遵守项目许可证,同时保留必要的版权和来源说明。


系列结语

这个系列从安装 Hexo 和 Butterfly 开始,经过页面配置、一图流背景、Inject 组件开发和本站组件整理,最终走到独立主题设计。

最稳妥的路线不是一次性重写,而是让每一次修改都具备清晰边界:

1
2
3
4
5
6
文章属于 source/_posts
资源属于 source
站点规则属于 _config.yml
主题规则属于 _config.butterfly.yml
个人增强属于独立 CSS / JavaScript
生成自动化属于 scripts 和 tools

当这些边界已经稳定,独立主题就不再是推倒重来,而只是把已经形成的个人设计逐步接管。


参考资料与声明

本文结合本站当前主题结构与长期维护需求整理。部分结构与文字由 AI 协助完成。


上一篇:本站自定义组件合集 · 返回系列总目录 · 下一篇:无