Hexo NexT 主题 8.x 版本的使用配置与美化

0. 前述

将近两年时间了,Hexo NexT 主题升级到了 8.13。本来早就应该进行例行更新了,但因为学业的原因,一直拖到了现在。NexT 主题再 7.x 版本进行了非常多的更新,但历史总是惊人地相似。原团队再次放弃了 NexT 仓库的维护,为此,NexT 主题再次进行了分支。

历史总是惊人地相似

相比于 NexT 7.x 版本,NexT 8.2+ 再次进行了大量的更新,并且不再支持之前的版本。Hexo 与 NexT 兼容性以及 NexT 各版本间的差异可以查阅官方说明

相比于当初对 NexT 7.7 主题的少量修改,NexT 8.x 内置了更多的样式与功能,同时提供了非侵入式的修改方法。接着科研的间隙,我再次将 Hexo 和 NexT 主题的版本进行了升级,并将用到的功能和改动介绍如下,做一个备忘。由于 Next 8.x 的功能已经非常完善,建议先阅读官方文档。然后,我所应用到的功能都会尽量提供非侵入式的修改,以保证代码的纯洁。

1. 安装与使用

安装好 Hexo 后,将 NexT 主题文件克隆或者拷贝到 hexo/theme 目录下即可。NexT 8.x 中做了很多修改,与之前版本相差较大,请进行全新安装。

1
2
cd hexo
git clone https://github.com/theme-next/hexo-theme-next themes/next7

2. 配置功能

1. 可以直接使用的特性

Next 8.x 中实现了大量特性或将大量功能通过插件实现,具体的用法与特点官方文档写的很详细,请自行对照查看。下面,我列举几条本站的主要修改。

2. 相关文章

NexT 依赖的 hexo-related-posts插件暂时不支持 Hexo 6, 要等待官方修复。
另外,另一款类似的插件 hexo-related-popular-posts倒是没限制 Hexo 版本,但在 NexT 不生效。

3 . 不蒜子统计提示语

在主题的_config.yml 中,已经内置了单词统计

4. 媒体播放器

Hexo 是静态博客,不支持媒体文件的播放。之前一直是使用[网易云音乐][l4]和[哔哩哔哩][l5]提供的ifream作为媒体播放器,但网易云音乐支持的歌越来越少了。而且用ifream引用也会遇到因为加载速度不一致报错的问题。因此在去年的新年贺词中,就首先使用了Aplayer播放器,不过`mmedia`插件也不错,综合了Aplayer和Dplayer(一款插入B站视频的脚本),后期会进行迁移。
安装方法:

1
2
3
4
5
6
7
8
// 安装aplayer,https://github.com/MoePlayer/APlayer
npm install --save hexo-tag-aplayer
// 安装mmedia, https://github.com/MonoLogueChi/hexo-tag-mmedia
// 如果安装了aplayer或者dplayer,需要先卸载
npm uninstall hexo-tag-dplayer
npm uninstall hexo-tag-aplayer
// 安装
npm install hexo-tag-mmedia --save

# 3. 我做的修改

1. 文章结束处添加感谢阅读的提示

  1. $NexT-ROOT/_config.yml 中取消 postBodyEnd 的注释

    1
    2
    custom_file_path:	
    postBodyEnd: source/_data/post-body-end.njk
  2. $Site-ROOT/source/_data/ 下新建 post-body-end.njk,写入如下内容,保存。

    1
    2
    3
    <div>
    <div style="text-align:center;color: #ccc;font-size:14px;">-----文章到此结束&nbsp;<i class="fa fa-paw"></i>&nbsp;感谢您的阅读------</div>
    </div>
  3. 结束,重新编译并刷新网页查看效果

2. 引入自定义的 JS 脚本

NexT 8.x 提供了非侵入式的修改方法,并且推荐使用这种方法对主题进行修改。在本文中,我们尽量地使用这种方法进行修改。在 3.1 中已经进行了尝试。本节将介绍如何引入自定义的 JS 脚本。在之后的教程中所有用到的 JS 脚本都会被引入并独立存放在网站目录。

  1. $NexT-ROOT/next/_config.yml 中取消 head.njk 的注释

    1
    2
    custom_file_path:	  
    head: source/_data/head.njk
  2. $Site-ROOT/source/_data/ 下新建 head.njk,写入如下内容,保存。

    1
    <script type="text/javascript" src="example.js"></script>

    这里写入的 <script> 标签其实就是正常的引入 JS 脚本的标签,example.js 是需要引入的自定义 JS 脚本文件的路径。在后续的修改中,遇到需要应用 JS 的脚本,只要在 head.njk 文件中添加一行 <script> 标签进行引入即可。

    3. 可选:关于引用本站 JS 脚本发生注入导致失效

<script> 标签中 JS 脚本的路径缺省为当前站点时,如 assets\js\example.js 时,可能被其他插件引用的 JS 文件注入,既在 example.js 中的第一行插入一句 <script> 标签。这是非法的,会导致 JS 出错并阻挡网站后续所有的 JS 脚本执行,造成网站的崩溃。目前发现 Aplayer 插件会发生这样的问题。
在进行诸多尝试后,本站决定绕过这一问题。将 JS 脚本托管在其他域名下,如 github.io,或者去 freenom申请同名的 tk 域名等,然后将 JS 脚本放在其他域名下。当跨域时,插件注入就不会发生,从而避免冲突。

3. 页面底部的部分部分内容调整。

在页脚可以展示阅读时间、访问人数、网站版权等信息,但是和第一行的备案内容相比,显得头重脚轻。
Next 默认页脚样式
因此通过修改底部的布局来调整。打开 $SNexT-Path\layout\_partials\footer.njk,将

1
<div class="wordcount">...</div>

代码段调整到

1
<div class="copyright">...</div>

之前,然后,把

1
<div class="busuanzi-count">.{.}.</div>

中间的内容添加到 wordcount 中,不包括最外层的 <div>。最后,就是效果啦。
调整后的页脚效果

2. 文章中添加对应的多语言版本的链接

这个功能其实之前几个版本都有实现过。如今,NexT 官方提供了动态的切换按钮,不过位置比较尴尬,在页面的底部,不好看。
Next默认语言选择器
我其实想用一种非侵入的方法将其调整一下,不过后来发现臣妾做不到啊。这个比较复杂,我之前在 8.11.1 上测试的,后来升级到 8.12.3 都忘记了改了哪里。所以记录的比较详细,大家按照步骤来即可。
NexT 官方提供的仅是切换到对应语言的主页,不支持切换到对应的文章页面。为了实现能够切换到对应语言的文章页。我这里按照以前的思路进行了修改,这个功能仍然需要手动设置相对应文章的 abbrlink 一致。建议在本地先编译一个语言版本的文件,然后手动修改另一个版本的文章的链接。
下面开始操作。

  1. 打开 $Site-ROOT/_config.yml,添加

    1
    2
    3
    language: 
    - zh-CN
    - en
  2. 打开 $NexT-ROOT/_config.yml 中找到 language_switcher: 标签,打开,并添加两个新字段。

    1
    2
    3
    language_switcher: true
    in_footer: false
    link_format: true # Whether to modify the language link format
  3. 找到 $NexT-ROOT\layout\_layout.njk,找到 {%- include '_partials/languages.njk' -%},修改为:

    1
    2
    3
    {%- if theme.in_footer %}
    {%- include '_partials/languages.njk' -%}
    {%- endif %}

    4 . 打开 $NexT-ROOT\layout/_partials/languages.njk,找到

    1
    2
    3
    4
    5
    {% for language in languages %}
    <option value="{{ language }}" data-href="{{ i18n_path(language) }}" selected="">
    {{ language_name(language) }}
    </option>
    {% endfor %}

    ,修改为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     {% for language in languages %}
    {%- if theme.in_footer.link_format %}
    <option value="{{ language }}" data-href="{{ i18n_path(language) }}" selected="">
    {{ language_name(language) }}
    {% else %}
    <span id="language" style="display:none">{{ language }}</span>
    <option value="{{ language }}" data-href="{{ i18n_path(language) }}" selected="">
    {{ language_name(language) }}
    {%- endif %}
    </option>
    {% endfor %}
  4. 打开 $Next-ROOT\layout\_macro/sidebar.njk,找到 site-overview-wrap 部分,

    1
    2
    3
    4
      <div class="site-overview-wrap sidebar-panel">
    {{ partial('_partials/sidebar/site-overview.njk', {}, {cache: theme.cache.enable}) }}
    {{- next_inject('sidebar') }}
    </div>

    ,修改为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
      <div class="site-overview-wrap sidebar-panel">
    {{ partial('_partials/sidebar/site-overview.njk', {}, {cache: theme.cache.enable}) }}
    {{- next_inject('sidebar') }}
    <!--language_switcher-->
    {%- if theme.in_footer %}
    {% else %}
    <div class="site-overview-item">
    {%- include '_partials/languages.njk' -%}
    </div>
    {%- endif %}
    <!--/language_switcher-->
    </div>
  5. 至此,所有的模块都已经整理完成了,接下来还要对语言选择器中的链接进行劫持。新建 $Site-ROOT/assets\js\language.js,写入如下内容。

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
window.onload=function(){
var now=new Date();
// todayHour=now.todayHour;dayth=now.dayth;
var lunar = calendar.solar2lunar(now.todayYear, now.todayMonth, now.todayDate);
// var k=Math.ceil(todayHour/2)%12;
// console.log(lunar)
var dayth=getDateNum();
// var k = getSign();
if (dayth == 1) {
console.log("Happy new year!")
img=jieqiImgArr[0]
} else {
img=jieqiImgArr[lunar.TermInx + 1]
}
var mybody = document.body
mybody.style.backgroundImage="url("+img+")"

// language_switcher
var root=document.location;
var sel=document.getElementsByClassName("lang-select");
var options=document.getElementsByClassName("lang-select")[0].options;
var domain=getLocation(root.hostname);
console.log(domain)
for (var i=1;i<options.length;i++)
{
var org_url=options[i].dataset.href;
console.log(org_url)
var org_urls=org_url.split("/");
console.log(org_urls)
console.log(org_urls.slice(2).join('/'))
var out_url=root.protocol+"//"+org_urls[1]+"."+domain+"/"+org_urls.slice(2).join('/');
console.log(out_url)
options[i].dataset.href=out_url;
}
}

function getLocation(root){
var arr = root.split('.');
if(arr.length === 2 || arr.length ===1 ){
return root;
}
if(arr.length > 2 && arr[0] !== 'www'){
return arr.slice(1).join('.')
}
return arr.slice(1).join('.')
}

  1. 结束,重新编译运行,查看效果。
    修改后语言选择器

2. 修改颜色样式,并添加时效背景

本站支持根据节气自动更换相关的主题壁纸,与一般的动态背景不同,带来新鲜感的同时也减轻了网站的负担。

自定义背景与样式

  1. 打开 $Next-ROOT/_config.yml,找到 custom_file_path,打开style处的注释

  2. $Site-ROOT/source/_data/ 下新建 styles.styl,写入如下内容,保存。

    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
    //添加背景图片
    body {
    background-image:url(/images/backGround.jpg);
    background-repeat: no-repeat;
    background-attachment:fixed;
    background-position:50% 50%;
    // background-size: 100% 100%;
    background-size: cover;
    -webkit-background-size: cover;
    -o-background-size: cover;
    -moz-background-size: cover;
    -ms-background-size: cover;
    }
    //改掉题头颜色
    .site-meta {
    background: #F0D784; //修改为自己喜欢的颜色
    }
    //主标题颜色
    .brand{
    color: #2f9833
    }
    //副标题颜色
    .site-subtitle{
    color: #47b54a
    }
    //页脚添加透明灰色背景
    .beian, .copyright, .wordcount{
    background: #999999b8
    color: #fff
    }
    // 修改页脚的超连接颜色与
    .beian a{
    text-decoration:none;
    }

    .beian a:link,
    .beian a:active,
    .beian a:visited{
    color:#fff;
    }
    .beian a:hover{
    color:#5bdf27;
    }
  3. 结束,重新编译运行,查看效果。

    根据时间更换背景

  4. 本站根据农历节气更换背景。思路是首先设定自定义的背景,然后使用 JS 劫持 background-image 的链接,换成需要的背景。本文中以 24 节气为例。同样的思路也可以实现 24 小时轮换壁纸或 12 时辰轮换壁纸,可参考旗下网站时光

  5. 因为需要节气日期,使用一个工具 JS 来根据当天日期获取农历日期与所在节气。在 $Site-ROOT/source/_data/head.njk 写入如下内容,保存。

    1
    <script type="text/javascript" src="//time.luov.top/assets/js/calendar.min.js"></script>
  6. 新建 $Site-ROOT/assets\js\jieqi_background.js,写入如下内容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var jieqiImgArr = [
    , "../images/0.jpg" //1 小寒
    ...//省略
    , "../images/23.jpg" //24 冬至

    ];
    // 设立一个数组存储图片的路径,从小寒开始。

    window.onload=function(){
    var now=new Date();
    var lunar = calendar.solar2lunar(now.todayYear, now.todayMonth, now.todayDate);// 根据今天的日期获取农历对象
    var dayth=getDateNum();
    img=jieqiImgArr[lunar.TermInx ]
    var mybody = document.body
    mybody.style.backgroundImage="url("+img+")"
    }

其他小修改

1. 解决左边工具栏上无法跳转到外部链接的问题

由于 NexT6 做了设置,左边工具栏上的所有链接将会自动在前面添加上当前域名。对于,“首页”,“关于” 这样的链接没有问题。但如果要添加上外链,例如英文页面:https:\\en.xian6ge.cn,会被自动编译为 https:\\xian6ge.cn\https\en.xian6ge.cn,造成跳转出错。为此,我们可以采用一个折中的办法。

hexo\source 下建立一个 en.html 文件,在里面通过 JavaScript 代码跳转。但这样会遇到一个问题,Hexo 会编译所有文件,造成其中的 Js 代码失效。因此,在前面添加 layout: false 设置,告诉编译器不要编译该 HTML 文件。具体代码如下。

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
layout: false
title: "XianliuGe - Eternal charm, Endless sound."
date: 2018-11-13 09:12:12
---
<!--上述title采用英文网站的title,以减少跳转时的突兀感。-->
<!DOCTYPE html>
<html lang="en">

<head>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>XianliuGe - Eternal charm, Endless sound.</title>
<script type="text/javascript">
window.location.href = "https://en.xian6ge.cn";
</script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->

<!--[if gte IE 9]>
<style type="text/css">
.gradient {
filter: none;
}
</style>
<![endif]-->

<!-- Favicon and touch icons -->
<!-- <link rel="shortcut icon" href="assets/ico/favicon.png">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="assets/ico/apple-touch-icon-144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="assets/ico/apple-touch-icon-114-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="assets/ico/apple-touch-icon-72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="assets/ico/apple-touch-icon-57-precomposed.png"> -->

</head>

<body>

<!-- Loader -->
<h1>前往『贤柳阁』英文版</h1>
<h2>Go to XianliuGe for English</h2>

</body>

</html>

2. 增加网站运行时间统计

这次没有在页脚处增加统计,避免过多的 js 代码拖累网站速度,将这段统计的代码放到关于中。但这个页面是由 Markdown 文件编译的,无法执行 js 代码。又不想单独重写这一页面,于是使用 ifream 的技术方法。

  1. about/ 文件夹下新建 timer.html,写入如下内容
    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
    layout: false
    title: "网站运行计时器"
    comments: false
    permalink:
    ---

    <html lang="en">
    <style type="text/css">
    *{margin:0;padding:0}
    .post-body{overflow-wrap:break-word;line-height:2;text-align:justify;font-size:1.125em}
    @media(min-width:992px){
    .post-body{text-align:justify}}
    @media(min-width:1200px){
    .post-body{font-size:1.125em}}
    div{display:block}
    .note{margin-bottom:20px;position:relative;border-radius:3px;padding:15px;border-width:1px;border-style:solid;border-image:initial}
    .success{color:#3c763d;background:#dff0d8;border-color:#d0e6be}
    .primary{background:#f3daff;border-color:#e1c2ff;color:#6f42c1}
    p{letter-spacing:normal;margin:0 0 20px}
    p:last-child{margin-bottom:0}
    p:first-child{margin-top:0}
    </style>
    <script language=javascript>
    function siteTime(){
    window.setTimeout("siteTime()", 1000);
    var seconds = 1000;
    var minutes = seconds * 60;
    var hours = minutes * 60;
    var days = hours * 24;
    var years = days * 365;
    var today = new Date();
    var todayYear = today.getFullYear();
    var todayMonth = today.getMonth()+1;
    var todayDate = today.getDate();
    var todayHour = today.getHours();
    var todayMinute = today.getMinutes();
    var todaySecond = today.getSeconds();
    /* Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
    year - 作为date对象的年份,为4位年份值
    month - 0-11之间的整数,做为date对象的月份
    day - 1-31之间的整数,做为date对象的天数
    hours - 0(午夜24点)-23之间的整数,做为date对象的小时数
    minutes - 0-59之间的整数,做为date对象的分钟数
    seconds - 0-59之间的整数,做为date对象的秒数
    microseconds - 0-999之间的整数,做为date对象的毫秒数 */
    var year = 2018//document.getElementById("year").innerHTML;
    var month = 02//document.getElementById("month").innerHTML;
    var day = 13//document.getElementById("day").innerHTML;
    var hour = 00//document.getElementById("hour").innerHTML;
    var minute = 00//document.getElementById("minute").innerHTML;
    var second = 00//document.getElementById("second").innerHTML;//北京时间2018-2-13 00:00:00
    var t1 = Date.UTC(year,month,day,hour,minute,second);
    var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
    var diff = t2-t1;
    var diffYears = Math.floor(diff/years);
    var diffDays = Math.floor((diff/days)-diffYears*365);
    var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
    var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
    var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
    if(diffYears==0){
    document.getElementById("sitetime").innerHTML=/*+diffYears+" year "*/+diffDays+" 日 "+diffHours+" 小时 "+diffMinutes+" 分 "+diffSeconds+" 秒";
    } else{
    document.getElementById("sitetime").innerHTML=diffYears+" 年 "+diffDays+" 日 "+diffHours+" 小时 "+diffMinutes+" 分 "+diffSeconds+" 秒";
    }
    }
    //siteTime(document.getElementById("year").innerHTML,document.getElementById("year").innerHTML,document.getElementById("year").innerHTML,document.getElementById("year").innerHTML,document.getElementById("year").innerHTML,0);
    siteTime();
    </script>
    </head>

    <body class="post-body">
    <div class="note primary">
    <p>『贤柳阁』已经运行了 <span id="sitetime">已经运行了一年多了</span></p>
    </div>
    </body>

    </html>
  2. 打开 about\index.md,在需要展示的地方增加
    1
    2
    3
    <span style="width:100%; height:260;border:none;text-align:center"> 
    <iframe allowtransparency="yes" frameborder="0" width="100%" height="88" src="/about/timer.html"/>
    </span>
    其中,iframesrc 标签中指向的是需要展示的 Html 文件,注意文件中要添加 layout: false,避免文件被编译。

至此,所有的修改完成,效果可以查看本站。未尽与错漏之处还请包含,欢迎在邮件或 Github与我联系。