介绍使用 Hexo 搭建博客的经验。
我在年初把博客迁移到了 ruhoh,用了一段时间之后,也开始感觉到各种不满意的地方。
就我个人的感受,ruhoh 有以下几个不足:
出于以上种种原因,我决定弃用 ruhoh ,改用其他的博客系统。
我第一个尝试的是 Jekyll
附图 1 Jekyll ,因为它本身也是基于 Ruby on Rails 框架,符合我的习惯,并且它的确很受欢迎。相比 ruhoh 而言,Jekyll 就显得非常原始了。刚开始启动服务器后,整个页面几乎啥都没有。所幸用 Jekyll 的用户很多,有很多现成的案例可供参考。加上自身使用 Liquid 模板语言,扩展起来也挺方便。但正当我基本移植了原来的主题,开始预览原来写的文章后,我发现了一个问题:Jekyll 的刷新居然是每更新一篇文章就要重新编译整个网站!于是我对 Jekyll 彻底失去了使用的兴趣。之后我又尝试了 Python 系的 pelican,总体感觉也很不习惯。后来在微博上 popozhu 向我推荐了 hexo ,一个基于 node.js 的博客系统。本来我就对 node.js 非常感兴趣,于是立马去 hexo 的主页了解更多它的特性。在看了它的文档之后,我立马把它列为我的 first choice 。与 Ruhoh 相比,hexo 有以下几个优点:
–bb
)。加上作者的项目管理非常合理,不仅有比较完善的项目主页和文档,也有很好的插件wiki,然后又有 node.js 的 npm 社区支持,感觉非常 promising 。虽然 hexo 看起来很不错,但由于 Ruby on Rails 和 node.js 毕竟是两个不同的平台,加上两个博客系统本身设计的不同,我在试图进行移植的时候也遇到了不少困难:
第一件我急着要解决的事情是将 hexo 原来的 Markdown renderer 改成用 pandoc。由于我原先的所有文章都是用 pandoc-markdown 来实现的,而自带的 renderer 则只支持最原始的 Markdown,也就是 John Gruber 定义的版本。因此直接使用自带 的 renderer 就会产生很多格式上的错误。但后来证明最急着要完成的事情往往是最难以完成的事情。在摸清楚了插件的编写方法后,我在 npm 社区上找到了一个 pandoc 的 wrapper,然后写插件调用它。但最初实现的版本工作起来很不稳定,总会随机性的出现部分页面没有渲染完整的问题。不过起码先有一个 work 的版本了,就暂时先用着它,等最后主题也移植完成了,我再回过头来解决它。于是我先给 hexo 提交了一个 issue 看看有没有人能够在我之前先解决了这个问题。
等真到了这个时候,我才发现事情没有那么简单。首先我怀疑是 hexo 的多线程编译跟 pandoc 的子进程发生了冲突,于是试着关闭 hexo 的多线程功能,并且将最大同时打开文件数设为1,但重启后发现问题依旧。之后我怀疑数据在传参过程中发生丢失,就试着把每一步都用 console.log
打印出来,发现也没有问题,只在我用回调函数返回的时候才出现随机的数据未处理情况。后来我一怒之下将原来的 wrapper 直接重写到我的插件里,也顺手把原来只能通过两层回调函数才能实现的调用改成了一层,问题终于解决了。我也就自己 close 了那个 issue,并在 npm 社区发布了一个 hexo-renderer-pandoc 插件。
另外一个让我在迁移过程中感到水土不服的地方是 hexo 没有 collection 的概念。ruhoh 的一大特色是为文章提供了 collection 的抽象,每个 collection 是彼此独立的,各自提供了 categories,tags,paginations 等。因此可以很容易在同一个站点划分不同的板块。
例如,我的博客 和 我的wiki 实际上共享了同一个 ruhoh 的站点,只不过博客里的文章属于 posts
这个 collection ,而 wiki 里的文章则属于 wiki
这个 collection ,两个板块的文章可以各自有相应的分类和标签,但不会有交叉。
而 hexo 并没有 collection 这样的概念。虽然 hexo 支持在 source 目录里放多个目录,但只有 _posts
目录的文章可以通过 site.posts
获取,其他目录的只能是一些 page。看样子只能新建一个站点用来处理 wiki ,但这样我又得为其专门写主题,而且两个站点各自都得用 hexo server
来预览 ,并占用不同的端口。更麻烦的是由于两个站点共享同一个导航栏(navigation),但两个站点在本地预览的时候不是同一个端口号,那么我还得根据当前的工作模式以及当前站点的端口号写一堆代码来实现导航栏。总而言之我非常崩溃。不过后来我想了一个workaround,因为只有我的 wiki 里的文章才有 categories 变量,其他 pages 没有这个变量。所以可以根据这个把 wiki 里的文章和其他 pages 区分开,再根据 categories 属性分类。于是我便写了段简单粗暴的代码,总算搞定了 wiki 页面。另外,原来我在 ruhoh 一直无法解决的自定义文章排序问题也在 hexo 里完美解决了。
主题的移植相对来说就只是个体力活了。hexo 本身自带的 light 主题已经非常完善,但出于对 twitter-bootstrap 的好感,以及对我原来花心思写好的主题的不舍,我决定还是大刀动斧,重写了整个主题的方方面面。相比 ruhoh 而言,hexo 的主题非常模块化,由于ejs允许在不同模板间传参,所以代码的冗余度可以降低(当然如果一味追求低耦合,又会带来模板文件数量太多而难以定位的缺点),完全不像 ruhoh 那样,partial 和主题各自属于不同的文件夹,但是逻辑上又难以完全分离,总让我感觉很诡异。
再说说 cache
变量吧,大部分的服务器都会提供 development
和 production
两种工作模式,比如如果当前是属于本地开发预览状态,还没有发布到远程服务器上的话,这个状态就是 development
模式,如果正在编译产生最终要发布到远程服务器的版本,系统就会把当前工作模式认定为 production
模式。利用这两个模式可以做很多有意思的事情。比如,如果我想在本地开发预览的时候使用未压缩的样式文件,而在正式编译的时候改用预先用 YUICompressor 压缩后的版本, 在 ruhoh 中可以这么做:
1 | {{# env.production }} |
而 hexo 虽然没有开放类似 ruhoh 的 env
接口,但由于它使用的是 express 作为服务器,可以通过 cache
变量来获取当前的模式——如果 cache
为 true
,则表示当前处于 production
模式,否则则处于 development
模式。因此可以这么写:
1 | <% if (cache) { %> |
hexo 本身是自带代码高亮的,但 hexo 自带的代码高亮功能似乎不能完全高亮我原先用 google-prettify 高亮的代码。于是我只好写了个 google-prettify 的高亮插件,并继续沿用我稍微定制过的 tomorrow-night 主题。但这个主题和 gist 的样式会有冲突,而 gist 的样式又无法修改,无奈之下,我想通过 jquery 来动态将 gist 的样式也设为与 tomorrow-night 一致的风格,后来我发现原来已经有人写过这样的插件,叫做 pretty-gist (完美主义者大有人在呀,哈哈),并且自身也集成了代码高亮,看了它的代码后,我稍微修改了下,让它改为使用 google-prettify 样式,最终的效果非常和谐,见上一节 #主题 的两段 gist 代码。
hexo 号称支持 Octopress 的所有 tag plugins,我仔细阅读代码后发现作者不过是用 javascript 移植了原本用 ruby 写的代码。仔细阅读了每个 tag plugin 的写法后,我发现这样的插件写起来也不难。比如,我的文章右侧的注解是自己实现的一个插件,可以通过下面的语法来调用:
1 | {% mnote index %} |
因为我使用 Emacs 作为主要编辑器,为了进一步减少工作量,还可以编写一个 snippet:
1 | # name: margin-note |
这样我每次要写注解的时候,只需要输入 mnote
然后敲一下空格,就可以补全成上面的插件代码,这个代码再经过编译就会展开成最终的 html 代码。关于 tag plugins 和 yasnippet 的结合可以参考这篇文章。plugin + snippet的结合非常爽快,所以我自己又写了不少插件,见这里。我打算在接下来把 twittter-bootstrap 常用的组件都写成插件,方便我使用。
上面罗列了大大小小的各种定制,感觉都有点不务正业了。接下来就想赶紧回到正轨,开始我的毕业论文的工作,在同时把新博客用上,不断地总结我学到的新东西。当然,现在的博客还有一些不完美的地方,接下来也想试着去完成:
--bb
;