搭建Hexo博客进阶

搭建Hexo博客进阶

更换模板

到themes目录下,下载自己喜欢主题模板

1
git clone https://github.com/probberechts/hexo-theme-cactus.git

在项目的_config.yml文件中进行更换主题

1
theme: hexo-theme-cactus

在项目目录下,打开git bash,输入以下命令,清除缓存,生成静态文件,查看效果

1
2
3
hexo clean
hexo g
hexo s #相当于 启动服务 hexo server

新建一个导航栏的项

在./themes/cactus/_config.yml找到nav

image

image

更换语言类型

更换项目的_config.yml 为下面的内容即可

1
2
3
4
5
6
7
8
# Site
title: 兀自
subtitle: ''
description: ''
keywords:
author: Garckz
language: zh-CN
timezone: ''

修改欢迎页面的内容

先查看你language用的语言模板,这里是:zh-CN

所以修改主题文件夹下面的language文件下面的zh-CN.yml文件的内容即可。

添加搜索(XML)

先在在./themes/cactus/_config.yml找到nav添加一个标签。

1
2
3
4
5
6
nav:
home: /
about: /about/
articles: /archives/
projects: http://github.com/probberechts
search: /search/

用到了 hexo-generator-search 的 Hexo 插件来做内容搜索,安装命令如下

1
npm install hexo-generator-searchdb --save

image

执行下面命令创建一个

1
hexo new page search

进入项目地址下的source/search/index.md,修改md文件的头为

1
2
3
4
---
title: Search
type: search
---

image

出现页面之后,搜索到的内容不可以超链接点过去访问,这时候需要进行以下的配置。

配置1

在项目下的 _config.yml 文件中,新增以下的配置项:

1
2
3
4
5
6
#Search
search:
path: search.xml
field: post
content: true
format: html

image

  • path:表示搜索后生成的文件路径,可以生成xml和json两种格式。

  • field:表示搜索的范围,有“​​post、page和all​​”三种值。

1
2
3
post:所有的文章;
page:所有顶部导航选项的页面;
all:所有的文章和顶部导航选项的页面。
  • content:是否包含搜索到的文章的全部内容。如果false,生成的结果只包括标题和创建时间这些信息,没有文章主体。默认情况下是true.
  • format:搜索到的内容、选项的格式。
1
2
3
html(默认):将html原文本缩略。
striptags:将html原文本缩略,并删除所有标记。
raw:记下每一篇文章或每一页的文字。

配置2

主题文件下_config.yml文件添加下面的内容

all:所有的文章和顶部导航选项的页面。

image

添加搜索(JSON)

上面我们使用.xml的方式做搜索。偶时候链接错误,出现undefined。

网上的说法是:某些文章有格式问题,导致生成的search.xml也出错了。

下面我们使用json格式的方式来实现该功能。

第一步

在项目的_config.yml文件中把search.xml修改为search.json

1
2
3
4
5
6
7
8
9
原本:
search:
path: search.xml
field: post

修改为:
search:
path: search.json
field: post

第二步

找到主题的search.ejs模板文件,我这里的路径是:themes/hexo-theme-cactus/layout/_partial/search.ejs

我这里的这个主题不需要修改。其它主题的修改方式:

1
2
3
4
5
6
7
<script type="text/javascript">
$(function () {
console.log("lets go!");
console.log("<%= config.root %>");
searchFunc("<%= config.root %>" + "search.xml", 'searchInput', 'searchResult');
});
</script>

修改为:

1
2
3
4
5
6
7
<script type="text/javascript">
$(function () {
console.log("lets go!");
console.log("<%= config.root %>");
searchFunc("<%= config.root %>" + "search.json", 'searchInput', 'searchResult');
});
</script>

最后一步

修改你的search的本地js文件,我这里的路径是:themes/hexo-theme-cactus/source/js/search.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
原本:
dataType: "xml",
success: function(xmlResponse) {

修改为:
dataType: "json",
success: function(datas) {


还需要注释掉:
// get the contents from search data
// var datas = $("entry", xmlResponse).map(function() {
// return {
// title: $("title", this).text(),
// content: $("content", this).text(),
// url: $("link", this).attr("href")
// };
// }).get();

最终结果为:

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// A local search script with the help of
// [hexo-generator-search](https://github.com/PaicHyperionDev/hexo-generator-search)
// Copyright (C) 2015
// Joseph Pan <http://github.com/wzpan>
// Shuhao Mao <http://github.com/maoshuhao>
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA
//
// Modified by:
// Pieter Robberechts <http://github.com/probberechts>

/*exported searchFunc*/
var searchFunc = function(path, searchId, contentId) {

function stripHtml(html) {
html = html.replace(/<style([\s\S]*?)<\/style>/gi, "");
html = html.replace(/<script([\s\S]*?)<\/script>/gi, "");
html = html.replace(/<figure([\s\S]*?)<\/figure>/gi, "");
html = html.replace(/<\/div>/ig, "\n");
html = html.replace(/<\/li>/ig, "\n");
html = html.replace(/<li>/ig, " * ");
html = html.replace(/<\/ul>/ig, "\n");
html = html.replace(/<\/p>/ig, "\n");
html = html.replace(/<br\s*[\/]?>/gi, "\n");
html = html.replace(/<[^>]+>/ig, "");
return html;
}

function getAllCombinations(keywords) {
var i, j, result = [];

for (i = 0; i < keywords.length; i++) {
for (j = i + 1; j < keywords.length + 1; j++) {
result.push(keywords.slice(i, j).join(" "));
}
}
return result;
}

$.ajax({
url: path,
// dataType: "xml",
dataType: "json",
//success: function(xmlResponse) {
success: function(datas) {
// get the contents from search data
// var datas = $("entry", xmlResponse).map(function() {
// return {
// title: $("title", this).text(),
// content: $("content", this).text(),
// url: $("link", this).attr("href")
// };
// }).get();

var $input = document.getElementById(searchId);
if (!$input) { return; }
var $resultContent = document.getElementById(contentId);

$input.addEventListener("input", function(){
var resultList = [];
var keywords = getAllCombinations(this.value.trim().toLowerCase().split(" "))
.sort(function(a,b) { return b.split(" ").length - a.split(" ").length; });
$resultContent.innerHTML = "";
if (this.value.trim().length <= 0) {
return;
}
// perform local searching
datas.forEach(function(data) {
var matches = 0;
if (!data.title || data.title.trim() === "") {
data.title = "Untitled";
}
var dataTitle = data.title.trim().toLowerCase();
var dataTitleLowerCase = dataTitle.toLowerCase();
var dataContent = stripHtml(data.content.trim());
var dataContentLowerCase = dataContent.toLowerCase();
var dataUrl = data.url;
var indexTitle = -1;
var indexContent = -1;
var firstOccur = -1;
// only match artiles with not empty contents
if (dataContent !== "") {
keywords.forEach(function(keyword) {
indexTitle = dataTitleLowerCase.indexOf(keyword);
indexContent = dataContentLowerCase.indexOf(keyword);

if( indexTitle >= 0 || indexContent >= 0 ){
matches += 1;
if (indexContent < 0) {
indexContent = 0;
}
if (firstOccur < 0) {
firstOccur = indexContent;
}
}
});
}
// show search results
if (matches > 0) {
var searchResult = {};
searchResult.rank = matches;
searchResult.str = "<li><a href='"+ dataUrl +"' class='search-result-title'>"+ dataTitle +"</a>";
if (firstOccur >= 0) {
// cut out 100 characters
var start = firstOccur - 20;
var end = firstOccur + 80;

if(start < 0){
start = 0;
}

if(start == 0){
end = 100;
}

if(end > dataContent.length){
end = dataContent.length;
}

var matchContent = dataContent.substring(start, end);

// highlight all keywords
var regS = new RegExp(keywords.join("|"), "gi");
matchContent = matchContent.replace(regS, function(keyword) {
return "<em class=\"search-keyword\">"+keyword+"</em>";
});

searchResult.str += "<p class=\"search-result\">" + matchContent +"...</p>";
}
searchResult.str += "</li>";
resultList.push(searchResult);
}
});
if (resultList.length) {
resultList.sort(function(a, b) {
return b.rank - a.rank;
});
var result ="<ul class=\"search-result-list\">";
for (var i = 0; i < resultList.length; i++) {
result += resultList[i].str;
}
result += "</ul>";
$resultContent.innerHTML = result;
}
});
}
});
};

最终项目的_config.yml

1
2
3
4
5
6
search:
path: search.json
field: post
content: true
format: html
limit: 10000

最终主题的_config.yml

1
2
3
4
5
6
7
8
local_search:
enable: true
# auto表示改变输入就自动触发搜索
# manual表示按下回车键或搜索按钮才触发搜索
trigger: auto
# 这里的数字n表示显示每篇文章搜索到的n个结果
# -1表示显示每篇文章搜索到的全部结果(不建议)
top_n_per_article: 1

添加标签页

1
hexo new page tags

image

根据上面的路径,找到index.md这个文件

添加type: “tags” layout: “tags”到内容中,添加后是这样的:

1
2
3
4
5
6
7
---
title: tags
date: 2023-04-29 21:30:23
type: "tags"
layout: "tags"
---

标签页实现标签云效果

安装插件

1
npm install hexo-tag-cloud  --save

找到 tagcloud.ejs 文件,完整路径:

修改为以下内容:

1

文章添加标签

在每篇文章的头部添加

1
2
3
4
5
title: Tags and Categories
date: 2017-12-24 23:29:53
tags:
- Foo
- Bar

image

新分类页

1
hexo new page categories

添加下面内容

1
2
3
4
5
6
7
---
title: categories
date: 2023-04-29 23:32:13
type: "categories"
comments: false
---

type 字段用来指定页面类型,comments 字段用来指定该页面是否显示评论。

主题文件的_config.yml的nav添加个导航栏

1
category: /categories/

striptags:将html原文本缩略,并删除所有标记。

先新建一个导航栏标签;在主题模板文件的_config.yml文件中设置

1
2
3
nav:
home: /
about: /about/

创建about页面

新建一个about的页面

1
hexo new page about

添加一些内容

1
2
3
4
5
6
7
8
9
---
title: about
date: 2023-04-29 21:49:29
type: about
---
### 单体应用存在的问题

- 随着业务的发展,开发变得越来越复杂。
- 修改、新增某个功能,需要对整个系统进行测试、重新部署。

image

新建文章

方法1:

定位到我们项目的根目

1
hexo new 'my-first-blog'

hexo会帮我们在“项目\source\_posts”下生成相关.md文件

image

1
2
3
4
5
---
title: my-first-blog
date: 2023-04-29 21:40:49
tags:
---

方法2:

直接自己打开“项目\source\_posts”新建.md文件

在书写的文章头添加上日期即可

1
2
3
4
5
6
title: Tags and Categories
date: 2017-12-24 23:29:53
tags:
- Foo
- Bar

image

博客访问量统计

进入./themes/hexo-theme-cactus/_config.yml添加下面一段代码

1
2
3
4
# 是否开启访问量统计功能(不蒜子)
busuanzi:
enable: true

进入./themes/hexo-theme-cactus/layout/_partial/footer.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
<footer id="footer">
<div class="footer-left">
<%= __('footer.copyright') %> &copy;
<% var endYear = (theme.copyright && theme.copyright.end_year) ? theme.copyright.end_year : new Date().getFullYear() %>
<% var startYear = (theme.copyright && theme.copyright.start_year) ? theme.copyright.start_year : new Date().getFullYear() %>
<%= startYear >= endYear ? endYear : startYear + "-" + endYear %>
<%= config.author || config.title %>
</div>
<div class="footer-right">
<nav>
<ul>
<% for (var i in theme.nav) { %><!--
--><li><a href="<%- url_for(theme.nav[i]) %>"><%= __('nav.'+i).replace("nav.", "") %></a></li><!--
--><% } %>
</ul>
<ul>
<% if (theme.busuanzi && theme.busuanzi.enable){ %>
<!-- 不蒜子统计 -->
<span id="busuanzi_container_site_pv">
本站总访问量<span id="busuanzi_value_site_pv"></span>次
</span>
<span class="post-meta-divider">|</span>
<span id="busuanzi_container_site_uv" style='display:none'>
本站访客数<span id="busuanzi_value_site_uv"></span>人
</span>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<% } %>
</ul>


</nav>
</div>
</footer>

image

image

1
2
3
4
5
6
7
8
9
<ul>
<% if (theme.busuanzi && theme.busuanzi.enable){ %>
<!-- 不蒜子统计 -->
<span id="busuanzi_container_site_pv">
本站总访问量<span id="busuanzi_value_site_pv"></span>次
</span>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<% } %>
</ul>

文章添加图片

安装插件

1
npm install hexo-asset-image --save

修改_config.yml

打开hexo的配置文件_config.yml;找到 post_asset_folder,把这个选项从false改成true

1
2
#加载图片用到
post_asset_folder: true

修改index.js

文件:

Text
1
/node_modules/hexo-asset-image/index.js

修改内容1:

1
2
3
4
58
$(this).attr('src', config.root + link + src);
修改为:
$(this).attr('src',src);

修改内容2:

1
2
3
4
5
45
|| /^\s*\/uploads|images\//.test(src))) {
修改为:
|| /^\s*\/uploads|images1\//.test(src))) {

最终代码:

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
'use strict';
var cheerio = require('cheerio');

// http://stackoverflow.com/questions/14480345/how-to-get-the-nth-occurrence-in-a-string
function getPosition(str, m, i) {
return str.split(m, i).join(m).length;
}

hexo.extend.filter.register('after_post_render', function(data){
var config = hexo.config;
if(config.post_asset_folder){
var link = data.permalink;
var beginPos = getPosition(link, '/', 3) + 1;
var appendLink = '';
// In hexo 3.1.1, the permalink of "about" page is like ".../about/index.html".
// if not with index.html endpos = link.lastIndexOf('.') + 1 support hexo-abbrlink
if(/.*\/index\.html$/.test(link)) {
// when permalink is end with index.html, for example 2019/02/20/xxtitle/index.html
// image in xxtitle/ will go to xxtitle/index/
appendLink = 'index/';
var endPos = link.lastIndexOf('/');
}
else {
var endPos = link.lastIndexOf('.');
}
link = link.substring(beginPos, endPos) + '/' + appendLink;

var toprocess = ['excerpt', 'more', 'content'];
for(var i = 0; i < toprocess.length; i++){
var key = toprocess[i];

var $ = cheerio.load(data[key], {
ignoreWhitespace: false,
xmlMode: false,
lowerCaseTags: false,
decodeEntities: false
});

$('img').each(function(){
if ($(this).attr('src')){
// For windows style path, we replace '\' to '/'.
var src = $(this).attr('src').replace('\\', '/');
if(!(/http[s]*.*|\/\/.*/.test(src)
|| /^\s+\//.test(src)
|| /^\s*\/uploads|images1\//.test(src))) {
// For "about" page, the first part of "src" can't be removed.
// In addition, to support multi-level local directory.
var linkArray = link.split('/').filter(function(elem){
return elem != '';
});
var srcArray = src.split('/').filter(function(elem){
return elem != '' && elem != '.';
});
if(srcArray.length > 1)
srcArray.shift();
src = srcArray.join('/');

//$(this).attr('src', config.root + link + src);
$(this).attr('src', src);
console.info("src:" + src);//src:Fp7aKq--0PJJ6yD8kdJ6bU6csSL8UPnWsCQKPjaGIv0.png
console.info&&console.info("update link as:-->"+config.root + link + src);
}
}else{
console.info&&console.info("no src attr, skipped...");
console.info&&console.info($(this));
}
});
data[key] = $.html();
}
}
});