Sukka's Blog

童话只美在真实却从不续写

和别的类似的给自己的个人 Hexo 博客添加 Prettify 的教程文章不同,这篇文章是给 Hexo 主题适配 Google Prettify 的教程,虽然这两者在实际上并无太多不同。

为了让「Suka」主题正如我在 Slogan 介绍的那样是一个「Powerful」的主题,所以除了内置 Prism 代码高亮以外,至少还应该内置 Google Code Prettify 代码高亮。

首先是把 Google Code Prettify 添加到 Hexo 主题之中。查看 Google Code Prettify 的 README 上面的教程,按照 Google 提供的方法 ,只需要引入一个主题 css 文件和一个 prettify.js 就行。
Google 打包了一个 Prettify 的默认主题和一个通用方案版本的 prettify.js 代码 供开发者下载。解压以后丢进主题的 source 目录里。

为了主题的前端性能,所以我们只需要在文章页面(layout 为 post 的页面)引入文件。正好,Hexo 提供了这辅助函数 is_post() 判断当前页面的 layout,我们就不需要什么 if (page.layout === 'post') 了。所以可以直接用这样的代码完成 prettify.js 的按需加载。

<% if ((is_post()) ) { %>
<script src="<%- url_for('js/prettify.js') %>"></script>
<% } %>

光光引入 prettify.js 并不能完成代码高亮,还要需要“激活”代码才能开始让 prettify 开始渲染代码。Google 推荐在 <body> 上绑定 onload 事件来调用 prettify,就像这样:

<body onload="PR.prettyPrint()">

这个方案显然很 dirty,而且 不符合规范 (虽然我这里引用的规范是我自己写的)。如果你在其它类似的给 Hexo 添加 Prettify 的教程上介绍的方法,无一例外使用了 $(document).ready(),还有一些使用了 $(window).load(function()。但是「Suka」主题并没有使用 jQuery,所以只能用 VanillaJS 的方法。而且,和 $(document).ready() 可以在一个页面里反复使用不同,window.onload 方法在一个页面里只允许使用一次。
我的方案更加简单粗暴。因为 HTML 中的 <script> 标签是按照顺序执行的,所以我可以在页面底部添加 <script> 标签调用 Prettify,在执行的时候已经可以确保所有 DOM Tree 都已经建立好、可以调用 Prettify 去遍历 DOM Node 了,并不需要等待 onload 事件发生。

但是,Prettify 并不是指定元素选择器的方式来就决定渲染哪一部分,Google Code Prettify 使用的元素选择器是 .prettyprint,并需要添加 .linenums 作为行号。所以必须事先对 <pre> 标签带上这个 class。不过,我在为「Suka」主题添加 PrismJS 代码高亮的时候就是使用的 preprocess 的方案,通过在主题的 scripts 目录下编写了一段插件脚本、在执行 hexo s 或者 hexo g 的时候对代码进行预处理(打上 class)。到适配 Google Code Prettify 的时候,我不想再采用 preprocess 的方案了,我要用 JS 中遍历 <pre> 标签带上这两个 class。
在那些常见的为 Hexo 添加 Prettify 的教程中,他们一般会教你使用 $('pre').addClass('prettyprint linenums'); 方法。但是同样因为我没有用 jQuery,我需要使用 VanillaJS 的方法:

for (var i = 0; i < (document.querySelectorAll('pre') || []).length; i += 1) {
document.querySelectorAll('pre')[i].classList.add('prettyprint');
document.querySelectorAll('pre')[i].classList.add('linenums');
}

虽然看起来更长,但是这是纯原生的 JS 代码方法,而且 It works。只需要在这段代码之后再添加 prettyPrint(); 渲染就行了。最终代码就像这样:

<script>
var prettifyel = document.querySelectorAll('pre');
for (var i = 0; i < (prettifyel || []).length; i += 1) {
prettifyel[i].classList.add('prettyprint');
prettifyel[i].classList.add('linenums');
}
prettyPrint();
</script>

当然,因为 prettyPrint() 方法依赖 prettify.js,所以需要把它放在其后,放在 is_post() 的判断的里面。

用同样的判断方法按需添加 prettify 的主题 css 文件。但是测试之后发现,Google 提供的默认的 Prettify 的主题并不好看。所以在 Google 上面搜索 Prettify Theme,排名第一的结果就是 这个 ,上面提供了很多漂亮的代码高亮主题。那么就是它了!
下载 zip 解压丢进主题的 source/css/prettify-themes 目录里。但是用户需要能够自己挑选主题,所以需要在主题配置文件之中暴露一个接口。我在主题配置文件中添加了 prettify.theme,接着在 <head> 使用 <link> 标签添加 css 到页面:

<% if ((is_post()) ) { %>
<link rel="stylesheet" href="<%- url_for('css/prettify-themes/'+ theme.prettify.theme +'.min.css') %>">
<% } %>

然后跑一遍 hexo s 预览一下效果,稍微修改一下样式就可以了。

你可以在「Suka」主题的这两条 commit 中找到其它相关细节、比如如何适配行号和如何添加 Custom CDN 支持:12a3ce7 59ef7a9

本文作者 : Sukka
本文采用 CC BY-NC-SA 4.0 许可协议。转载和引用时请注意遵守协议!
本文链接 : https://blog.skk.moe/post/add-prettify-to-hexo/

本文最后更新于 天前,文中所描述的信息可能已发生改变