昨天晚上折腾了一下这个 blog 的主题,主要包含了一下几个更新。

  1. 增加了标签展示
  2. 增加了目录展示
  3. 增加了评论功能

这篇博客主要记录一下如何实现和一点心得。

主题的原型 Apollo

这个主题的前身是 apollo 主题。我特别喜欢它的简介,以及模仿 Vue 官方网站的样式,所以这个博客最开始的版本就直接使用了这个主题(只有一些非常简单的修改)。

在那个时候,我对 Hexo 也并没有什么了解,只是单纯的照着教程使用而已,没有办法对主题本身做比较明显的改动。

糙快猛地学习 Hexo

最近相对整个 blog 做些读者体验方面的改进。主要的动因是我的同事在搜索 Spanner 的文章的时候搜到了我的博客。一旦有了实实在在的读者,我的整个心态就有点飘了,准备添加一下之前我就觉得缺少的特性:标签和目录。为此,我就迅速地读了一下 Hexo 的文档。

Hexo 的文档说实话有点太简单了,而且样例代码都是基于 ejs 的。因为 apollo 本身是用 jade 实现的,而我又没有用过,所以还是遇到了一些麻烦,不过最后都通过看 Hexo 以及别的主题的代码搞定了。下面就讲讲两个更新的实现方式。

标签展示

展示标签其实挺简单的。Hexo 提供了 list_tags 这个函数,并且提供了一定的自定义空间,所以非常简单。

1
2
3
4
5
6
7
// 因为我只想在文章页展示,所以判断了一下现在的位置是否是 post
if is_post()
// 在每个 tag 前面加一个 #
- var transform = function(str) { return '#' + str; }
- var config = {class: 'post-tag', show_count: false, style: false, separator: ' ', transform: transform}
.post-tags
!= list_tags(item.tags, config)

这里我遇到的一个问题就是,我不知道 pug 怎么直接在 object 里面使用 lambda,只好把代码拆成了几行。不过这样可读性似乎也好一点。

目录功能

添加目录比较简单,Hexo 也提供了 toc 这个函数。然而,为了达到现在的效果,着实花了我一点时间。当然,对于有前端经验的朋友们来说,应该比较简单,我就不赘述了。这里我主要想说说怎么实现滚动的时候,目录会高亮当前展示的部分对应的目录项。

网上有挺多实现方式的,我挑选了 Bootstrap。Bootstrap 提供了 scrollspy 的功能,虽然有文档,但是文档中有几个属性的名字写错了,也花了我一点时间。下面是实现的几个主要部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 为 body 加上以下几个属性。
// data-bs-target 的值必须是目录元素的 id
body.container(data-bs-spy="scroll" data-bs-target="#toc-nav" data-bs-offset=0)
// ...

// 这是目录的定义。注意它的 class 必须是 navbar。
nav#toc-nav.navbar
// 这里的 class 必须是 nav。
!= toc(item.content, { list_number: false, class: 'nav' })

// 也别忘了 include bootstrap 的 js 文件。
// 也可以自己 host。
script(src="//cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.min.js")

中文的支持

如果你的 blog 和我一样,也是使用中文,那么只有上面的代码,是没法实现自动高亮的。这个问题的原因是 toc 是实现在指定链接的时候,会调用 encodeURL。然而,Bootstrap 会假设这个链接和文章中每个段落标题的 id 是相同的。这个 id 是由 hexo-renderer-marked 解析 markdown 生成 html 的时候自动生成的,默认就是这个段落的标题。所以,如果你使用的英文作为标题,encodeURL 之后可能还是一样的,Bootstrap 就能关联起来。

为了解决这个问题,我看了一下 hexo-renderer-marked 的文档,发现它支持自定义每个段落的 id。你需要在 _config.yml 中先启用 anchorAlias

1
2
3
marked:
headerIds: true
anchorAlias: true

然后在 Markdown 中,将原本的 # header 改成 # [header](#header-id)。这样就能解决这个问题了。

评论功能

评论功能的实现就很容易了,apollo 本身就支持。如果使用 disqus 的话,只要先去 disqus 上注册一个网站,然后把注册的网站名写进配置就可以了。