去年就开始说要更新博客主题了,拖到现在终于有点空闲时间和动力了,相信大家已经使用过 Hexo 了,安装之类的我都不多废话了,直接开始。
想要自定义一个 Hexo 主题大概需要了解一个 模板引擎,Hexo 内置了 Swig
,我所用的是默认安装的 EJS
,反正都差不多,都提供了一些模板语法,书写还是按照Js的方法来的。
然后先按照文档创建完主体目录结构和模板组件,就可以开始开发了,我比较偷懒没有使用 yeoman + generator-hexo-theme 来创建,直接复制了原先的 landscape
主题。
哦,对了!还要选择一个CSS预编译器,我还是没有选择适用了默认安装的 Stylus
,你也可以按照习惯去安装,并且 install
对应的 Hexo 插件。
# Templates
$ npm install --save hexo-renderer-ejs
$ npm install --save hexo-renderer-pug
$ npm install --save hexo-renderer-haml
# Styles
$ npm install --save hexo-renderer-less
$ npm install --save hexo-renderer-sass
$ npm install --save hexo-renderer-stylus
然后不需要配置,Hexo 会自动按照文件后缀名使用对应的拓展编译文件。
📚 前置学习
- 了解主题目录结构 👉 主题 | Hexo
- 模板页名称、局部变量的使用 👉 模版 | Hexo
- 全局变量和页面变量 👉 变量 | Hexo
- Hexo 内置辅助函数 👉 辅助函数(Helpers) | Hexo
以上四个文档希望你可以阅读完,特别是辅助函数,会在制作过程中反复用到,也是最关键的部分,我相信大家已经对 [html
, css
, js
] 三剑客很熟悉了,所以就不提他们了。
完整目录如下:
│
├─ layout # 布局文件夹
│ ├─ partials # 局部模版
│ │ └─ recent-posts.ejs # 列表页文章Item
│ ├─ index.ejs # 首页模板
│ ├─ layout.ejs # html结构
│ ├─ post.ejs # 文章列表模板
│ ├─ archive.ejs # 归档列表模板
│ ├─ category.ejs # 分类归档列表模板
│ ├─ tag.ejs # 标签归档列表模板
│ └─ page.ejs # 单页模板
│
├─ scripts # 脚本文件夹
│
├─ source # 资源文件夹
│ ├─ css
│ ├─ js
│ └─ favicon.ico
│
├─ .editorconfig # 编辑器配置文件
├─ _config.yml # 主题配置文件
└─ package.json # 项目包管理文件
Part1. 主题模板开发
首先会想到要着手的肯定是首页,但是直接开发首页肯定是不行的,还需要先把网页的结构搭完,好比现在 SPA 项目的 index.html
文件。
那么就会先就需要调整 layout.ejs
这个模板文件,打开的话,可以看到一个简单的 html
结构,这个时候就是需要按照自己的想法来搭建主题的公共部分了,
比如说 页首 与 页尾,然后使用 <%- partial(path, [locals], [option]) %>
来引入组件 locals和option的可接收参数文档。
我把 meta
信息也提取成部件了,完成大概是这样的结构。
<!-- layout page -->
<!DOCTYPE html>
<html lang="<%= config.language =>">
<%- partial('_partial/meta') %>
<body>
<%- partial('_partial/header', null, {cache: true}) %>
<div id="wrap">
<%- body %><!-- body变量,用于输出页面内容 -->
</div>
<%- partial('_partial/footer', null, {cache: true}) %>
</body>
</html>
然后开始编辑 index.ejs
这个文件,开始可以按照自己的喜好编辑首页了,记得使用 hexo s
命令开打开发模式查看效果。
我是顺着上一版本的主页,只是展示文章,所以直接把文章卡片抽成组件了,从 page.posts
接收到文章列表,使用 forEach()
循环出列表,然后使用 paginator()函数 把分页组件也写好了
呃….好像也没有什么好说的,就是和过去 CMS 时代用模板语法来写 template
那样,只是以前是嵌入 php
的代码然后用 php
来执行渲染,而 Hexo 是用 JS
来编译成静态文件。
我这边大概完成主题之后的目录结构如下:
layout
├─_partial # 小部件
│ │ archive.ejs # 文章分类列表组件
│ │ archive_post.ejs # 列表页文章卡片组件
│ │ article.ejs # 文章详情组件
│ │ footer.ejs # 页尾组件
│ │ header.ejs # 页头组件
│ │ meta.ejs # meta组件
│ │ sidebar.ejs # 侧边栏组件
│ │ soliloquy.ejs # 关于我组件
│ │
│ ├─_post # 文章组件
│ │ article_body.ejs # 文章内容部件
│ │ category.ejs # 文章分类部件
│ │ date.ejs # 文章日期部件
│ │ nav.ejs # 文章详情分页部件
│ │ tag.ejs # 文章标签部件
│ │ title.ejs # 文章标题部件
│ │
│ └─_widget # 侧边栏卡片组件
│ archive.ejs # 文章归档卡片
│ author.ejs # 个人信息卡片
│ category.ejs # 文章分类卡片
│ recent_posts.ejs # 最近发布卡片
│ shortcut_menu.ejs # 快捷分类菜单卡片
│ tag.ejs # 标签列表卡片
│ tag_cloud.ejs # 标签云卡片
│
│ about.ejs # 关于我模板
│ archive.ejs # 文章列表模板
│ category.ejs # 分类列表模板
│ index.ejs # 主页
│ layout.ejs # 布局模板
│ page.ejs # 单页模板
│ post.ejs # 文章详情模板
└─ tag.ejs # 标签页模板
需要注意的是一些默认的模板名称,hexo
会自动去寻找这些模板来生成对应的页面。模板文档 - Hexo
模板名 | 用途 | 回退 |
---|---|---|
layout | 布局 | - |
index | 首页 | - |
post | 文章 | index |
page | 分页 | index |
archive | 归档 | index |
category | 分类归档 | archive |
tag | 标签归档 | archive |
所以你的主题目录:
- 应包含一个
index
模板 - 如果要使用
layout
组件,必须要使用<%- body %>
输出模板的内容。
Part2. 配置文件设置
在开发主题的时候你会遇到一些属性可能全局会用到,比如说头部导航,页尾配置,侧边栏卡片显隐等,就可以配置 _config.yml
文件来实现全局变量 文档
Hexo的配置文件分为 站点配置文件
和 主题配置文件
,分别位于 项目根目录 和 主题根目录 下。
一般来说,全局的一些配置项需要放到 站点配置文件 中,比如说:站点配置,静态页面生成规则,插件配置等,然后通过 config.xxx
来获取&使用。
和主题有关的配置则放到 主题配置文件 中,比如:侧边栏开关,社交账户信息,头部导航等控制主题内容的部分,通过 theme.xxx
来使用。
我的想法是尽量减少对于站点配置文件的控制,能放到主题配置文件中的就放到主题配置文件中。
🎉 尾声
基本上关于主题开发的内容就那么多,整体来说只要你懂一些三剑客的基础很容易就可以自己开发出一个主题,只是有一些犄角旮旯需要你去查文档,有一些说明并不明显,需要耐心的从文档中检索出来。
如果需要发布你的主题,那么我会在另一篇文章中来记录。我先挖一个坑,因为自己制作完主题之后还没有按照文档规范走完测试流程。
遇到的问题
# 使用 is_current(path)
判断生成的 ‘active’ 类不会在对应分类添加,只会执行一次
查看引入的部件 <%- partial('partial/xxx',{ cache: <Boolean> }) %>
部分是否开启了缓存 👉 模版 | Hexo
# orderby
有哪些可选参数?
name
名称length
文章数random
随机排列
# 空白主题不支持 markdown
的 table
语法/看不到表格
因为没有设置 <table>
的样式,自己使用CSS写一个表格样式就好。
# 使用 hexo-prism-plugin
进行代码高亮处理,但是在列表页的预览内容中的代码块不会被处理
其实会被处理,只是我在用 hexo-theme-unit-test
测试的时候有使用 代码块标签插件
的测试文章,类似这样:
{% codeblock lang:js mark:1,7-8,10 %}
const http = require('http');
const hostname = '127.0.0.1';
const port = 1337;
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
{% endcodeblock %}
需要开启 prismjs
,默认是关闭的,在 _config.yml
中开启即可。