Hexo 搭建个人博客(完结)Hexo 相关问题和优化

目标

本文记录一些 Hexo 的难点问题及其解决方案。

环境及版本声明

本文基于以下环境及版本:

hexo: 3.8.0
hexo-cli: 1.1.0
NexT: 7.1.0
OS: Ubuntu 18.04 LTS x86_64

禁止爬虫跟踪外链

搜索引擎的蜘蛛来爬取文章内容时,如果你的文章中有外部链接,它就会到外链的站点去爬取,有可能再也不会回来了。为了告诉搜索引擎不要跟踪这些外链,需要在这些链接标签中添加属性 rel="nofollow"rel="external nofollow"

rel="nofollow" 是通用格式,即是告诉搜索引擎不要跟踪此链接,rel="external nofollow" 是更具体的写法,进一步告诉搜索引擎这是一个外部的链接,不要跟踪它。

我们可以 hexo-autonofollow 插件来给外链添加 nofollow 属性,这样有利于 SEO 。

安装 hexo-autonofollow

在站点根目录下执行下列命令:

npm install hexo-autonofollow --save

编辑 站点配置文件

站点配置文件 _config.yml 末尾添加如下内容:

# Adds nofollow attribute to all external links in your hexo blog posts automatically.
## https://github.com/liuzc/hexo-autonofollow
nofollow:
    enable: true
    exclude: # 例外的链接,可将友链放在此处
    - yourname.github.io # 排除你的站点
    # - 友链地址

配置后,在生成的静态文件中,例外的链接就不会被加上 nofollow 属性。

设置永久链接

链接层级过深、链接中包含中文、因为 title 变动导致链接也经常发生变动,这些都不利于 SEO 。推荐 permalink 的解决方案是使用插件 hexo-abbrlink 生成 permalink 。

安装 hexo-abbrlink

站点根目录下执行:

npm install hexo-abbrlink --save

编辑 站点配置文件

修改 站点配置文件 _config.yml 的 permalink 如下:

permalink: posts/:abbrlink/

其中 :abbrlink 代表连接地址。可以在 站点配置文件 下添加 abbrlink 的配置(其中 alg 和 rep 分别为生成的算法和表示方式),如下:

# abbrlink config
## https://github.com/rozbo/hexo-abbrlink
abbrlink:
  alg: crc32  # support crc16(default) and crc32
  rep: hex    # support dec(default) and hex

不同配置生成的链接效果如下:

算法进制结果
crc16hexhttps://post.zz173.com/posts/3ab2.html
crc16dechttps://post.zz173.com/posts/12345.html
crc32hexhttps://post.zz173.com/posts/9a8b6c4d.html
crc32dechttps://post.zz173.com/posts/1690090958.html

执行 hexo clean && hexo g 重新生成静态文件后,源文件 front-matter 中会包含 abbrlink: xxx

这样不论我们的文件名、文章的 title 、文章的内容有没有发生改变,abbrlink 都不会改变,同时对搜索引擎更加友好。

CRC 全称 Cyclic Redundancy Check,又叫循环冗余校验。CRC32 跟 MD5一样都是哈希算法的一种。hexo-abbrlink 的源码来看,实际上它并没有利用到时间,只是利用了文章 title 来生成。

使用这种方法生成 permalink 时,在每次提交修改前,最好先执行 hexo clean && hexo g,确保提交前你所有的文章的 front-matter 中都包含 abbrlink ,避免因 title 的改变导致生成 abbrlink 不一致(如果已存在 abbrlink,就不会重新生成,不论 title 是否发生变化)。

分类存放文章源文件

Hexo 默认所有的文章都会放在 source/_posts 文件夹下,当文章越来越多时,将很难管理文章源文件同时也不利于快速查找到想要的文章。

我个人的做法就是分层分类存放,同时利用上面提到的 abbrlink 来设置 permalink,以确保不管文章源文件如何变换分类、分类层次有多深,生成的静态页面的链接始终对 SEO 友好。

例如,在 _posts 文件夹,创建了 AlgorithmLinux 两个文件夹作为一级分类, Linux 分类下创建了 command 文件夹作为二级分类:

source fold

source unfold

其中,文章的同名同级的文件夹是它的资源文件夹,存放文章对应的资源,如图片等。

本地运行测试,查看文章 QuickSort 如下:

posts classify

不管源文件如何存放,链接始终是 /posts/xxxxxxx/ 的形式,另外需要注意 文章 front-matter 中的 categories 与这里的分类存放没有任何关系 ,如上图中 QuickSort 文章分类于 Algorithm -> Sort,而其 md 文件是直接放在了 Algorithm 目录下。

如果你想让 URL 直接对应源文件的分类也是可以的,但不推荐这么做,编辑 站点配置文件,修改 permalink 配置如下:

permalink: posts/:title/

重新生成,测试结果如下,注意 URL 与源文件存放位置的对应关系:

post quicksort

post netstat

英文引号变成中文引号

引号显示异常,英文单引号 ’ 双引号 ",都显示成中文单引号’ 双引号”。

方法一

因为 hexo-renderer-marked 渲染 Markdown 有很多问题,所以推荐使用方法二。

Hexo 默认使用 hexo-renderer-marked 作为 Markdown 的解析引擎,它的 smartypants 配置项默认为 true,只需将其禁用就可以解决引号显示异常的问题。

编辑 站点配置文件,在文件末尾添加如下配置:

# Prohibit English quotation marks from becoming Chinese quotation marks
## https://github.com/hexojs/hexo/issues/1981
marked:
  smartypants: false

保存修改,重新生成站点,就可以正常显示了。

方法二

Hexo 默认自带的 Markdown 渲染引擎 hexo-renderer-marked 有很多不足,例如在渲染数学公式时会有很多问题,同时因为它默认开启 smartypants,导致引号显示异常,所以最好的解决方案是更换 Hexo 默认的 Markdown 渲染引擎。

这里推荐使用 hexo-renderer-pandoc,这个插件依赖于 Pandoc,具体的操作可以参考下面 “数学公式渲染”。

更换渲染引擎后,无需修改任何配置,引号的显示就是正常的。

Hexo NexT 数学公式渲染

参考:

为什么要替换 Hexo 的默认 Markdown 引擎?

Hexo 默认使用 marked.js 去解析我们写的 markdown,比如一些符号,_ 代表斜体,会被处理为 <em> 标签,比如 x_i 在开始被渲染的时候,处理为 x<em>i</em>,这个时候 mathjax 就无法渲染成下标了。很多符号都有这个问题,比如粗体 *,也是无法在 mathjax 渲染出来的,好在有替代的乘法等,包括 \\ 同理。

目前,NexT 提供两种数学公式渲染引擎,分别为 MathJaxKatex,默认为 MathJax。

如果你选择使用 MathJax 进行数学公式渲染,你需要使用 hexo-renderer-pandoc 或者 hexo-renderer-kramed 这两个渲染器的其中一个。

这里推荐使用 MathJax + hexo-renderer-pandoc ,并以此为例:

本地测试

(1) 因为 hexo-renderer-pandoc 依赖 Pandoc,所以首先需要安装 Pandoc,具体安装步骤参考 how to install pandoc

(2) 站点根目录下执行下列命令,卸载原有的渲染器 hexo-renderer-marked,然后安装 hexo-renderer-pandoc

npm uninstall hexo-renderer-marked --save
npm install hexo-renderer-pandoc --save

(3) 编辑 主题配置文件,修改配置如下:

# Math Equations Render Support
math:
  enable: true

  # Default (true) will load mathjax / katex script on demand.
  # That is it only render those page which has `mathjax: true` in Front Matter.
  # If you set it to false, it will load mathjax / katex srcipt EVERY PAGE.
  per_page: false

  engine: mathjax

(4) 重新生成并测试:

hexo clean && hexo g && hexo s -o

Travis CI 配置

因为 hexo-renderer-pandoc 依赖 Pandoc,所以 Travis CI 中也要安装 Pandoc,而且最好安装较新的版本,比较旧的版本如 1.19.X 可能解析出错。

在 .travis.yml 中添加类似配置,参考 installing-packages-without-an-apt-repository

before_install:
  - wget https://github.com/jgm/pandoc/releases/download/2.7.2/pandoc-2.7.2-1-amd64.deb
  - sudo dpkg -i pandoc-2.7.2-1-amd64.deb

完整配置可参考 wylu.github.io/.travis.yml

文章更新时间异常

方法一:使用 updated 属性

推荐使用方法二,避免手动操作。

当使用 Travis CI 自动部署时,发现部署成功后,所有文章的更新时间都变成了此次提交修改的时间,但有些文章在上一次提交后是没有发生过任何修改的。另外,本地测试显示的更新时间是正常的。

这是因为 git 在推送更新时,并不记录保存文件的访问时间、修改时间等元信息,所以每次使用 git 把项目 clone 下来时,文件的时间都是克隆时的时间。又因为如果没有在 front-matter 中指定 updated,Hexo 会默认使用文件的最后修改时间作为文章的更新时间,所以会出现所有文章的更新时间都发生变化的情况。

总的来说,使用 git clone 下来的文件的时间都不是原来文件的时间,而 Travis CI 每次都需要 clone 源码才能进行后面的生成和部署操作,所以目前如果想正确显示更新时间,可以在 front-matter 中指定,或者改用本地手动推送部署。

下面是一个 python 脚本,用来向 front-matter 中插入最后修改时间。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import time

file_encoding = 'utf-8'


def last_modify(path):
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(os.stat(path).st_mtime))


def write2file(file_path, updated=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())):
    with open(file_path, 'r', encoding=file_encoding) as f:
        lines = f.readlines()
    updated = 'updated: ' + updated + '\n'

    flag = False
    i = 1
    while True:
        if lines[i].startswith('updated: '):
            flag = True
            break
        if lines[i] == '---\n':
            break
        i += 1

    if flag:
        print("===> Existed. " + file_path)
        return

    lines = lines[:i] + [updated] + lines[i:]
    with open(file_path, 'w', encoding=file_encoding) as f:
        f.write(''.join(lines))
    print("===> Success. " + file_path)


def get_paths(path):
    paths = []
    if os.path.isfile(path) and path.endswith(".md"):
        return [{"file_path": path, "updated": last_modify(path)}]
    for root, dirs, files in os.walk(os.path.join(path)):
        for file in files:
            if file.endswith(".md"):
                file_path = os.path.join(root, file)
                paths.append({"file_path": file_path, "updated": last_modify(file_path)})
    return paths


def add_update_time(path):
    if not os.path.exists(os.path.join(path)):
        return
    paths = get_paths(path)
    for i, item in enumerate(paths):
        print(i, end='')
        write2file(item['file_path'], item['updated'])
    print("Done.")


def main():
    global file_encoding
    argc = len(sys.argv)
    if argc < 2 or argc > 3:
        print("Error!\nUsage: python post_util <post directory> [file encoding], for example:")
        print('\tpython post_util.py /home/wylu/hexo/source/_post')
        print('\tpython post_util.py "/home/wylu/hexo/source/_post" utf-8')
        return
    if argc == 3:
        file_encoding = sys.argv[2]
    add_update_time(path=sys.argv[1])


if __name__ == '__main__':
    main()

指定 _post 文件夹路径,可批量操作,如:

python post_util.py /home/yourname/hexo/source/_post

如果 front-matter 中已存在 updated,则不会覆盖。如果想重新生成,可以删掉 updated 所在的行然后重新执行。

方法二:使用 git 推送时间

参考 NexT deployment .travis.yml

在 Travis CI 的配置文件 .travis.yml 中添加如下配置:

before_install:
  # Restore last modified time
  - "git ls-files -z | while read -d '' path; do touch -d \"$(git log -1 --format=\"@%ct\" \"$path\")\" \"$path\"; done"

实际上,clone 下来的文件的时间还是克隆时的时间,然后通过上面的命令,它将 clone 下来的文件的时间改成了该文件最近一次变动的推送时间(也即文件最后一次修改的 push 时间)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值