如何为hexo主题添加新layout


在使用小组内部的paper reading list的时候,突然想到应该给我自己的博客也添加一个类似的页面或功能,用来比较系统的整理一些知识点。
最初的idea只是想添加一个简单地页面,它默认情况下以列表的形式显示条目名称,而条目对应的具体内容则是被点击后才展开。但后来又想,如果每次添加或者删除某个条目时,都需要对这个页面进行修改,可能维护起来不太方便。
在找到Wixo这个hexo的主题后,我就知道我想要的那个页面就是这个样子的。
但我暂时还不想完全改变博客的主题,所以就需要将Wixo主题实现的功能给融合进现有的Landscape主题中,整个过程记录成此文。

1. Hexo主题的安装与使用


Hexo安装一个主题的方法非常简单,只需要在theme list中找准一个自己喜欢的主题,然后下载下来就可以了。
以默认landscape主题为例,具体步骤如下:

// 将源码下载到你博客项目的themes目录下  
$ git clone https://github.com/hexojs/hexo-theme-landscape.git themes/landscape
// 编辑_config.yml,将theme设置为landscape  
$ vim _config.yml  
  theme: landscape 

2. Landscape主题的源码结构


关于hexo主题的源码结构,在官方的文档:Themes中有比较详细的介绍,_config.yml是主题的配置文件,scripts文件夹主要用于存放javascript脚本,source中主要存放CSS,图片等内容,layout中则实现了该主题支持的layout。
在layout文件夹中是一些ejs文件,EJS是Embedded JavsScript的缩写,这些ejs文件完成的功能我理解成整个博客框架的模板。

layout.ejs:是整个博客的最基本的模板,所有的页面都在它基础上展开。  
从源码可以到,它主要就是为每一个页面定义了一个统一的最基本的框架,比如head,sidebar,footer。      
layout.ejs源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%- partial('_partial/head') %>
<body>
<div id="container">
<div id="wrap">
<%- partial('_partial/header', null, {cache: !config.relative_link}) %>
<div class="outer">
<section id="main"><%- body %></section>
<% if (theme.sidebar && theme.sidebar !== 'bottom'){ %>
<%- partial('_partial/sidebar', null, {cache: !config.relative_link}) %>
<% } %>
</div>
<%- partial('_partial/footer', null, {cache: !config.relative_link}) %>
</div>
<%- partial('_partial/mobile-nav', null, {cache: !config.relative_link}) %>
<%- partial('_partial/after-footer') %>
</div>
</body>
</html>

而最常见的post和page都是在layout.ejs的基础上,补全了body部分。而且从源码来看,post和page的layout细节上是完全一致的,唯一的不同是hexo对于post和page这两类layout的处理方式不一样,具体可以去看看文档。

1
2
3
4
post.ejs源码:
<%- partial('_partial/article', {post: page, index: false}) %>
page.ejs源码:
<%- partial('_partial/article', {post: page, index: false}) %>

3. 如何将wixo主题的layout添加进landscape主题?


通过大致分析了landscape主题layout的源码,具体怎么hack landscape主题就不难了。目前的实现方法如下:
a. 在layout文件夹中增加wiki-index.ejs这个layout,源码基本参照wixo的index.ejs
b. 将wixo需要的CSS和JS都放到landscape的相应位置,然后在wiki-index.ejs中引用;
c. 为博客建一个layout是wiki-index.ejs的post,然后将它的链接放到导航栏中;
d. 从下面wiki-index.ejs的实现中,可以看到我使用了一个trick来生成wiki页面。wiki-index页面只会解析名字是以”wiki-“开头的category。这可能会对原始博客的categories widget造成干扰,有待进一步改善。

wiki-index.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
<%- css(['css/wiki/bootstrap-responsive.css']) %>
<%- css(['css/wiki/bootstrap.min.css']) %>
<%- css(['css/wiki/font-awesome.css']) %>
<%- css(['css/wiki/google-fonts.css']) %>
<%- css(['css/wiki/responsive.css']) %>
<%- css(['css/wiki/sidenav.css']) %>
<%- css(['css/wiki/style.css']) %>
<%- css(['css/wiki/highlight.css']) %>
<%- js('js/wiki/jquery-2.0.3.min.js') %>
<%- js('js/wiki/jquery.imagesloaded.min.js') %>
<%- js('js/wiki/jquery.tableofcontents.min.js') %>
<%- js('js/wiki/gallery.js') %>
<%- js('js/wiki/main.js') %>
<%- js('js/wiki/bootstrap.min.js') %>
<%- js('js/wiki/tocgenerator.min.js') %>
<div class="row page">
<div class="col-md-12">
<div class="panel-group" id="notebook">
<% site.categories.sort('name').each(function(cat){ %>
<% if(cat.name.substring(0, 5) == 'wiki-') { %>
<div class="panel panel-default">
<div class="panel-heading">
<a class="panel-title" data-toggle="collapse" data-parent="#notebook" href="#<%= cat.name %>">
<i class="fa fa-folder"></i> <%= cat.name %>
</div>
<div id="<%= cat.name %>" class="panel-collapse collapse">
<div class="panel-body">
<%- list_posts({orderby:"date",order:1,count:65535,query:{categories:cat.name}}) %> </div>
</div>
</div>
<% } %>
<% }); %>
</div><!-- accordion -->
</div>
</div>

最后非常感谢Wixo的作者wzpan实现了这么美观又实用的一个主题。

参考资料


Hexo: Documentation
Wixo - a wiki theme for Hexo
Wixo demo
Wixo source code
Hexo: theme list
Hexo折腾笔记(二)博客优化与定制