看过《Hexo框架搭建和部署GitHub博客》想必你已经熟练的使用Hexo现成的主题搭建了博客!不过你挑来挑去也没遇到心仪的主题怎么办呢?你只要掌握一点点Html、css和js,甚至不需要懂得各种模板引擎,看完本篇相信你就能慢慢写出或者魔改别人的主题然后做出符合自己标准的主题。

本文不再详细讲解Html、css和js的基础语法,Hexo的文档也是挑着用到的说,用不到的希望读者自己去研究和查找

从零开始制作 Hexo 主题

为避免重复造轮子(手动狗头.jpg),这里贴出 Ahonn 在知乎上的教程《从零开始制作 Hexo 主题》,此教程必看,文章详细的讲解了主题的制作过程,并且用到的 hexo 项目预装了的 render 插件 ejs 和 stylus 也是非常的简单且容易上手。

魔改 apollo 主题

从零制作一个好看的主题对我来说是比较困难的,因为我主业也不是做web的,对我来说难度较高。在对 Hexo 官网贴出的 主题 进行挑选之后,我选中了极简主题 hexo-theme-apollo,这个主题比较简单比较简洁符合我的审美,接下来我讲一下我魔改这个主题的思路和过程。

首先我们看到主题 hexo-theme-apollo其实模板引擎用的是 jade ,CSS 预处理器用的是 scss,如果我们魔改的时候也用这两个东西的话无意增加了大量的学习成本(jade模板引擎语法相对学习成本比较大)。而预装的 ejs 模板引擎就非常的简单更容易理解,我贴出两段示例我们来看下:

1
2
3
4
5
6
7
8
9
10
<header>
<a href="/" class="logo-link"><img src="<%- url_for(theme.icon.header_logo) %>" alt="icon"></a>
<ul class="nav nav-list">
<% for (name in theme.menu) { %>
<li class="nav-list-item">
<a href="<%- url_for(theme.menu[name]) %>" target="_self" class="nav-list-link <%= is_current(theme.menu[name]) ? 'active' : '' %>"><%= name %></a>
</li>
<% } %>
</ul>
</header>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="tab1-div" class="archive" style="display: block">
<ul class="post-archive">
<% var last; %>
<% page.posts.each(function (post) { %>
<% var year = post.date.year(); %>
<% if (last != year){ %>
<% last = year; %>
<h2 class="archive-main-category"><%= year %></h2>
<% } %>
<div class="post-item">
<div class="post-info"><%= date(post.date, "YYYY-MM-DD") %></div>
<a href="<%- url_for(post.path) %>" class="post-title-link" target="_blank"><%= post.title %></a>
</div>
<% }) %>
</ul>
</div>

这个模板就很简单了,无非就是“<% %>”夹着一些js代码而已,而且里面的js代码很多是调用的 Hexo 的 API,而且和 html 结合之后,看起来更直观更简单。并且 Hexo 的官方文档上对 API 的介绍示例也是使用的 ejs 模板引擎的写法。

不过让我坚持使用 ejs 模板的都不是之前说的那些原因。最重要的原因是这个:如果我想魔改或者模仿一个现成的主题或者网页来写主题,怎么样才能最快的达到别人的效果呢?很简单,在谷歌浏览器打开这个网站,然后按 F12 查看 html 源码,照着它的源码来写 ejs 模板,然后用到的 css 也一并拿过来(需要使用 js 的地方不多不做过多阐述),这样的话就可是很快的模仿别人的网站来写自己的主题,毕竟 ejs 模板其实也就是 js + html 而已,用 js 的逻辑和 Hexo API 来生成html。

从我魔改 hexo-theme-apollo 主题然后写出自己的 hexo-theme-umatobu 主题的时候我遇到了很大的困惑,其他地方都好魔改,唯有一处非常难处理,就是在 archive 页面来按照文章分类来生成列表,我通过查 Hexo 文档(注意是英文文档发现是可以添加多级分类的,中文稳定很久没有更新),且 post.categories API 是可以拿到对应文章的分类信息的,不过这个 API 返回的是 Object,需要自己根据内部数据来自定义操作。因为 ejs 不支持 JSON 函数,这里我利用大量的 typeof() 和 Object.keys() 方法来分析这个 Object 的数据结构,大致是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
post.categories(Object)

|-- Object
|-- data(Object)
|-- 0(Object)
|-- name
|-- _id
|-- slug
|-- path
|-- permalink
|-- posts
|-- length

|-- 1(Object)
...

...

|-- length(number)

不管你以以下这两种写分类的方式的任意一种来写文章分类,上面的这个结构都是在 data 下面在 +1

1
2
3
categories:
- [main_category, minor_category]
- ...
1
2
3
4
categories:
- category1
- category2
- ...

所以这里我为了能把我的 hexo-theme-umatobu 主题的归档页面加上按照分类信息来分类的功能,我这里所有的文章必须加上一下分类信息:

1
2
categories:
- [main_category, minor_category]

我先遍历所有的文章把分类结构拿出来,然后再次遍历所有文章把是这个分类的文章贴在这个分类下方(注意我这个写法只支持二级分类,更多层级的不支持)。我在 archive.ejs 这样来提取分类数据和归类:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<div id="tab2-div" class="archive">
<ul class="post-archive">
<% var category_obj = new Object(); %>
<% page.posts.each(function (post) { %>
<% var one_post_category_obj = new Object(); %>
<% for(var i = (post.categories.length-1); i >= 0; i--) { %>
<% if( i == 0) { %>
<% category_obj[post.categories['data'][i]['name']] = one_post_category_obj; %>
<% } else { %>
<% var temp_obj = one_post_category_obj; %>
<% temp_obj[post.categories['data'][i]['name']] = temp_obj; %>
<% one_post_category_obj = temp_obj; %>
<% } %>
<% } %>
<% }) %>

<% var category_keys = Object.keys(category_obj); %>
<% for(var i in category_keys) { %>
<h2 class="archive-main-category"><%= category_keys[i] %></h2>
<% var category_keys_temp = Object.keys(category_obj[category_keys[i]]); %>
<% for(var j in category_keys_temp) { %>
<div class="subtitle-item">
<label><%= category_keys_temp[j] %></label>
</div>
<% } %>
<% page.posts.each(function (post) { %>
<% if(post.categories.length == 0) { %>
<% } else if(post.categories.length == 1) { %>
<% if(post.categories['data'][0]['name'] == category_keys[i]) { %>
<div class="post-item">
<div class="post-info"><%= date(post.date, "YYYY-MM-DD") %></div>
<a href="<%- url_for(post.path) %>" class="post-title-link" target="_blank"><%= post.title %></a>
</div>
<% } %>
<% } else { %>
<% if(post.categories['data'][0]['name'] == category_keys[i] && post.categories['data'][1]['name'] == Object.keys(category_obj[category_keys[i]])[0]) { %>
<div class="post-item">
<div class="post-info"><%= date(post.date, "YYYY-MM-DD") %></div>
<a href="<%- url_for(post.path) %>" class="post-title-link" target="_blank"><%= post.title %></a>
</div>
<% } %>
<% } %>
<% }) %>
<div class="post-item">
</div>
<% } %>
</ul>
</div>

注意注意注意!!!

此篇是有问题的,最终出来的分类归档页面的次级分类只有最后那个,分析了很久也没找到原因,索性推倒重来,下篇会解决这个问题。需要分类归档页面的请看下篇。

不过此篇也没有删掉的必要,也能学到一些东西!