Hexo 自定义主题

标签: Hexo 分类: 前端 创建时间:2018-11-16 08:50:50 更新时间:2025-01-17 10:39:22

从这一篇文章开始,我想基于Hexo的Next主题自己写,打造一个个性化的博客。一点点的尝试吧,搞不好,总可以搞坏的。

将Next主题从网上下载下来,放到hexo网站的themes文件夹下,并命名next,修改网站_config, 将theme,改为theme:next。运行命令hexo s,打开浏览器localhost:4000查看效果。我自己定义的主题,大多借鉴Next主题。Next主题主要使用了node-swig作为hexo的模板,要想好好的看懂Next,需要知道简单的语法规则:学习链接 (刚发现swig好像不维护了,哎,还没学会,就被淘汰了。nunjucks好像语法差不多和siwg: api ) ,而且hexo最新版本也换成了nunjucks,我觉得语法差不多,就是换一个文件后缀名的问题,但是也不排除有不一样的行为, Next使用的css模板是Stylus模板( stylus-lang.com )

参考文章:
1.根据设置生成CSS,这里有个地方注意下“hexo提供了hexo-config方法,可以读取_config.yml中的配置”,所以你可以在styl文件中经常看到这个函数,你要单独搜这个东西,好像还真是不明显。
2.在js插件中访问设置,在js插件中访问设置

1.修改网站标题和作者

打开hexo的_config.yaml配置文件,不是主题的配置文件,开头几行就是。title,subtitle,author.

## 2、选择Next主题的风格

Next主题包含了四种风格,分别是Muse、Mist、Pisces、Gemini,将需要的主题前面的#号去掉,不需要的主题使用#注释掉。

3.自定义主题

主要解决的问题是:

3.1.熟悉主题的结构

--+themes
-----+themename
--------+languages            //国际化语言,在swig文件中,通过__(变量名)调用,变量名极为zh-CN.yml中的变量名
-----------+zh-CN.yml         //
-----------+en.yml
-----------+.......
--------+layout               //布局页面,相当于html
-----------+index.swig        //首页的模板
-----------+post.swig         //文章的模板
-----------+page.swig         //new page 模板
-----------+category.swig     //归档页模板
-----------+tag.swig          //标签页模板
-----------+。。。。。          //可以添加其他的模板或文件夹,便于相互之间使用swig语法引用
---------+scripts             //hexo会在生成页面时(即执行hexo g命令时)自动调用scripts文件夹下的js文件,可以写些js做些自动化
---------+source              //主题的资源文件夹
-----------+js                //主题的js代码所在位置
-----------+css               //主题的css所在位置
-----------+images            //主题的图片所在位置
-----------+fonts             //主题的字体位置
-----------+。。。。            //其他文件或文件夹,方便相互引用

hexo在生成静态文件时,会自动寻找layout中的index.swig文件,生成首页,然后找到post.swig,根据post.swig生成文章页面,同理,page.swig就是使用“hexo new page ‘new page’”命令时产生的page页面的模板。swig文件可以相互引用,在index.swig中可以使用其他的swig文件,也可以自定义,在swig文件中可以使用hexo的辅助函数和变量。在swig文件中使用的图片和js脚本通常放到source文件夹中,在swig可以直接通过引入script标签引入js,或者是css文件,其中js即为source文件夹下的js文件夹,lib即为source文件夹下的lib,在生成页面时,hexo会自动处理复制。

1
2
3
4
5
6
//引入css文件
<link href="css/main.css" rel="stylesheet" type="text/css" />

//引入js文件
<script type="text/javascript" src="js/src/page_index.js"></script>
<script type="text/javascript" src="lib/jquery.js"></script>

source下的css文件夹存放hexo可能会用到的css文件,再css这一级的更目录下可以自由添加任何css文件,hexo在生成文章时会自动将文件复制到public/css文件夹中,添加的styl类型文件会被编译后复制到public/css文件夹中,文件或文件夹名称带下滑线,不会自动复制,引用时可以在styl文件中通过@import引入,hexo就可以自动处理了。

hexo默认会生成tags(标签目录)、categories(分类目录)、archives(归档目录),查看生成的public目录即可。每一个目录下都有一个index.html页面,是根据tag.swig、category.swig模板生成的,没有定义相应的模板,就用默认的index.swig模板生成。

3.2.熟悉swig语法

生成文章时的执行顺序如下:

1
2
3
4
执行 before_post_render 过滤器
使用 Markdown 或其他渲染器渲染(根据扩展名而定)
使用 Nunjucks 渲染
执行 after_post_render 过滤器

swig文件没有了,可以使用Nunjucks渲染,模板语法官网

3.3.活用hexo辅助函数和变量

3.4.主页中如何进行分页

首页(index),变量为page,我觉得这个page不仅仅代表着首页,也代表着每一个目录页,tag、categories。

3.5.自定义菜单栏

这里解决的一个主要问题我觉得要明白如何进行分页,例如新建一个菜单项,打开这个菜单项时,出现和首页一样的分页效果。这里有参考资料, 主要思想就是用归档页代替page页,因为hexo的归档页(category)和标签页(tag)是支持分页的,也就是可以在category.swig和tag.swig中使用page.posts,在主配置文件中也有index_generator,和default_category支持配置归档页和标签页每页显示的文章数,根据这个思路,那么菜单栏,每一项要支持分页,菜单栏的路径就应该指向一个归档目录,例如:catetories/随笔

3.6.关于国际化

现在看起来好像是在生成页面以前,就进行了一个merger,在next主题文件夹的scripts文件夹下,但是他执行的原理和方法,还没有详细研究,等有机会再说吧。

3.7在首页上添加了一个缩略图的效果

实现的原理就是通过在主题文件家下新建一个文件夹scripts,将js文件写到里面,hexo生成文章的时候会自动执行这里面的js文件。我自定义了一个过滤器,after_post_render,在每一篇文章生成后,都会调用这个函数,在函数里面,我抽取了文章的content中的img标签的src,并把他放到了变量page.photos里面,这样在index.swig里面就可以使用这个page.photos了,默认是没有这个里面是个空数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
hexo.extend.filter.register('after_post_render', function(data){
let content=data.content;
//匹配图片(g表示匹配所有结果i表示区分大小写)
var imgReg = /<img.*?(?:>|\/>)/gi;
//匹配src属性
var srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i;
var arr = content.match(imgReg);
if(arr&&arr.length>=0){
for (var i = 0; i < arr.length; i++) {
var src = arr[i].match(srcReg);
//获取图片地址
if(src[1]){
data.photos.push(src[1]);
}
}
}

return data;
});

3.8.分页插件(添加layui分页插件)

首先引入layui组件的js,在index.swig(或者其他被引入的swig文件)

1
<script type="text/javascript" src="{{ url_for(theme.js) }}/src/page_index.js"></script>

然后在index.swig模板文件中添加以下代码,主要含义即通过将page分页信息挂载到dom元素的属性上,然后再通过js文件动态生成分页信息。目前没有找到直接在js中获取hexo全局变量的方法。

1
2
3
<div class="pagination_wrap" data-totalpage="{{page.total}}" data-current="{{page.current}}" data-perpage="{{per_page}}">
{% include 'pagination.swig' %}
</div>

3.9.添加评论

添加评论这个挺简单的,只需要按照参考文章1中的内容进行配置就好了。

我这里还有一个问题,就是有些文章中没有评论,但是会显示一些相关评论的文章,看起来非常的难看,我想着直接把这个给清掉好了。

(1) 第一种方式就是直接使用代码进行控制。

1
2
3
4
5
6
7
8
9
10
11
12
function loadComments () {
var d = document, s = d.createElement('script');
s.src = 'https://https-bibichuan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', '' + +new Date());
(d.head || d.body).appendChild(s);

// 隐藏相关文章
s.onload = function () {
var relatedCommon=$('#comments #disqus_recommendations');
relatedCommon.hide();
}
}

(2) 第二种方法就是,可以通过 Disqus 按右上角的 Admin 進入後台管理,选择要管理的站点,然后找到 Settings,选择Recommendations,这里面有相关的配置。可以关闭显示,甚至可以直接关闭

How do I manually set the images for my Recommendations 这里也显示如何配置图片

参考文章:
1.Disqus评论插件 用这个代码就好了
2.一起来踩配置 Disqus 的坑
3.Hexo NexT 添加 Disqus 评论区
4.获取多说和 Disqus 文章评论数的方法 这里就是显示评论数的一种方法
5.Disqus 有评论但没显示的一种解决方案
6.如何安裝 Disqus 留言板 最簡單的方法就是到後台設定語言,請到 Disqus 按右上角的 Admin 進入後台管理。找到 Settings > General 後,向下拉會找到「Language」項目,選擇好語言後按下方藍色的「Save」保存,留言板的語言就更新成功了!

3.10.遍历显示文章的tags,categories(解决错误Unhandled rejection TypeError: Converting circular structure to JSON)

我打算在文章的首页文章列表的上,显示每篇文章的类别和标签,我尝试使用

1
2
3
{% for categ in post.categories%}
<span class="categories">{{categ}}</span>
{% endfor%}

但是不幸的是出现了“Unhandled rejection TypeError: Converting circular structure to JSON”错误。
于是我在上面(7)的基础上return data之前加入了

1
2
3
console.log(data);   
console.log(data.categories);
console.log(data.categories.data);

一步步输出,终于查到了,post.categories的数据格式。

在github上有这么一个解决方案:issues/2189 但是实际上,对于swig模板来说,本身是没有forEach这个函数的。

1
2
3
post.categories.forEach(function(item){
console.log(item.name);
});

终于在一篇博客中发现了蛛丝马迹:hexo-theme-guide ,我就差一个.name了

1
2
3
4
5
6
7
8
{% set i = 0 %}
{% for cat in post.categories %}
{% if i !== 0 %}
{{'/'}}
{% endif %}
{% set i = i + 1 %}
<a href="{{ url_for(cat.path) }}">{{ cat.name }}</a>
{% endfor %}

3.11.根据分类自动生成网站顶部菜单

为了处理自定义的菜单和系统分类菜单,我在第七步的过滤器中添加了如下函数在过滤器template_locals中添加,将在theme配置文件中自定义的菜单type_menu和文章分类合并成了一个headermenu变量,注入到locals中,在文章中即可使用site.headermenu变量,通过遍历这个数组,可以生成一个菜单目录。

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
var _uniqBy=require("lodash/uniqBy");
hexo.extend.filter.register('template_locals', function(locals){
locals.now = Date.now();
//生成页面顶部菜单栏
let menu=[];
let theme=hexo.theme.config;
let type_menu=theme.type_menu;
for(let menuItem in type_menu){
let subObj=type_menu[menuItem];
if(typeof subObj=='object'){
let subMenu=[];
for(let sub in subObj){
subMenu.push({"name":sub,"path":subObj[sub],"sub":false});
}
menu.push({"name":menuItem,"path":"","sub":subMenu});
}else{
menu.push({"name":menuItem,"path":subObj,"sub":false});
}
}

let categories=hexo.locals.get("categories").data;
for(let menuItem in categories){
let name=categories[menuItem].name;
menu.push({"name":name,"path":"/categories/"+name+"/"});
}
menu=_uniqBy(menu,"name");
// hexo.locals.set('headermenu', function(){
// return menu;
// });
locals.headermenu=menu;
return locals;
});

其实主要思想就是,我把自定义的菜单和hexo生成的文章分类都放到一个menu数组里了,然后根据这个数组生成一个菜单,这个菜单可以说相当复杂,如果菜单数超过4个,就把剩下的放到更多里面,如果没有超过四个,那就挨个排列显示。如果菜单有子菜单,还要把他放到dd标签里面,再更多这一栏的下拉菜单中,还有可能生成三级菜单。首先判断菜单数是否大于4个,不大于3个,就直接遍历生成菜单,如果有子菜单的话,就多套一层。如果菜单数大于4个,先是遍历,把前四个生成出来,然后添加一个更多按钮,再遍历一次菜单数组,将前4个过滤掉,从第四个开始遍历,挨个添加到更多这一个菜单中,如果在遍历的过程中,发现这个菜单有子菜单,那就再遍历一遍子菜单生成一层,如果没有子菜单了,就直接输出菜单明就好了。(费老劲了,都不知道这有啥用。)

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<nav class="site-nav">
<ul class="menu layui-nav">
{% for menuItem,path in theme.menu %}
<li class="menu-item layui-nav-item">
<a href="{{ url_for(path) }}" rel="section">
<i class="iconfont icon-home"></i>
{{- __('menu.'+menuItem) -}}
</a>
</li>
{% endfor %}
{% set menuCount=3 %}
{% if site.headermenu.length>=menuCount%}
{% set menuItemIndex=0 %}
{% for menuItem in site.headermenu%}
{% if menuItemIndex <= menuCount %}
{% if menuItem.sub!=fase %}
<li class="menu-item layui-nav-item">
<a href="javascript:;">{{menuItem.name}}</a>
<dl class="layui-nav-child">
{% for subItem in menuItem.sub %}
<dd class="nav-child">
<a href="{{ url_for(subItem.path)}}">{{subItem.name}}</a>
</dd>
{% endfor %}
</dl>
</li>
{% else %}
<li class="menu-item layui-nav-item"><a href="{{ url_for(menuItem.path)}}">{{menuItem.name}}</a></li>
{% endif %}
{% set menuItemIndex = menuItemIndex + 1 %}
{% endif %}
{% endfor %}
<li class="more menu-item layui-nav-item">
<a href="javascript:;">更多</a>
<dl class="layui-nav-child">
{% for menuItem in site.headermenu%}
{% if loop.index0 >= menuItemIndex %}
{% if menuItem.sub!=fase %}
<dd class="nav-child">
<a href="javascript:;">{{menuItem.name}}</a>
<span class="nav-more"></span>
<dl class="nav-child-child">
{% for subItem in menuItem.sub%}
<dd><a href="{{ url_for(subItem.path)}}">{{subItem.name}}</a></dd>
{% endfor %}
</dl>
</dd>
{% else %}
<dd class="nav-child">
<a href="{{ url_for(menuItem.path)}}">{{menuItem.name}}</a>
</dd>
{% endif %}
{% endif %}
{% endfor %}
</dl>
</li>
{% else %}
{% for menuItem in site.headermenu%}
{% if menuItem.sub!=fase %}
<li class="menu-item layui-nav-item">
<a href="javascript:;">{{menuItem.name}}</a>
<dl class="layui-nav-child">
{% for subItem in menuItem.sub %}
<dd class="nav-child">
<a href="{{ url_for(subItem.path)}}">{{subItem.name}}</a>
</dd>
{% endfor %}
</dl>
</li>
{% else %}
<li class="menu-item layui-nav-item"><a href="{{ url_for(menuItem.path)}}">{{menuItem.name}}</a></li>
{% endif %}
{% endfor %}
{% endif %}
</ul>
</nav>

实现的效果如下:

接下来的计划

1.404页面添加
2.seo优化
3.用vscode编写hexo还是有点不方便,修改文章标题,修改文章文件名、复制照片都有点不方便,需要写个应用程序。

小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。