制作第一个hexo主题

制作第一个hexo主题

Hexo是一个知名的静态博客生成工具,尤其在github用户中广为人知。hexo的主题可以自由变换,但是想要找到一个完全合自己心意的主题仿佛大海捞针。与其不停寻找,不如自己动手制作一个主题,既解决了自己看什么主题都不合心的问题,还能在其中学习更多知识。

这里是我写的主题toki以及预览。如果你觉得这篇文章对你起到了一点作用,欢迎你star或者fork进行进一步的学习,不必担心难度,因为这也是我第一个hexo主题。

hexo主题结构

目录结构如下:

 .
├── _config.yml   
├── layout        
│   └── _partial  
└── source        
    ├── css
    ├── fonts
    └── js

其中config文件是存储与主题相关的变量,如点赞、评论功能是否开启,在整个站点下也有一个config文件,它是用来存储一些和整个站点有关的,比如站点的标题。而并非所有的hexo主题都支持点赞,所以点赞功能的开启放在具体的theme下更合适。

Layout文件夹下存放的是布局模板文件,可以用ejs、jade等编写,里边包含index、post等hexo中默认使用的模板,也可以编写一个自己额外定义的页面,比如about页。对于这些自己额外编写的模板,需要用户自己执行hexo new [layout] <title>中声明layout:about才可以。_partial子文件夹中存放的是一些可以服用的组件或者想要细分的部分,如打赏部分、点赞部分,可以分离出来,使得结构更加清晰明了。

Source文件夹下存放的是资源文件,比如css文件,字体文件,图片文件等。source文件在最后生成的时候会把其中的文件都直接放在根目录下,所以在文件中引用地址的时候不需要写source,而是直接写./css/app.css就可以。

hexo变量和函数

想要编写一个hexo模板,我们在其中必定要添加一些hexo站点下的信息,如网站的title。还有我们之前提到的主题下也有一个config文件,其中的变量我们也需要调用。除了以上两个部分,hexo还提供了一些用来简化操作的辅助函数。
具体的变量和辅助函数可以访问hexo官网。这里只介绍一些常用的。

变量:

  • page.title
  • site.posts
  • page.excerpt
    关于整个站点下的config文件需要通过config.xxx来进行调用,对于这个主题的config文件需要通过theme.xxx来调用。而诸如page、site等就是hexo自身的一些变量了。

函数:

  • <%- list_categories([options]) %>
  • <%- titlecase(string) %>

模板文件

Hexo已经定义的模板文件有以下几个:

  • index
  • post
  • page
  • archive
  • category
  • tag
    特别的还有一个layout文件,可以在layout文件中编写一些公共部分,实现在其他页面中的复用,如果你对模板引擎并不了解,我建议你先学习相关知识是非常必要的,不过不用太多,花十几分钟大概理解一下就可以。
    当然你并不需要都要包括这些模板,只是写了这些模板文件后,你不需要hexo new它,它也有固定的页面地址。每个主题最少可以只包括一个index页面即可。但是为了功能上比较完善,我还是建议最少编写index、post、archive这三个页面。
    在hexo中编写文件,需要选择一种模板引擎,这里我使用的ejs,hexo自己提供的ejs的支持,而且理解起来也很容易,没什么选择其他的理由。当然如果你想要编写的主题比较庞大,那还是选择自己最顺手的好。
    我们分别讲解index、post、archive的编写,然后在进行拓展。

layout页面

这一页要包含菜单、页脚声明。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title><%= config.title %></title>
             <!-- 引入站点的标题-->
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <%- css('./css/app.css') %>
          <!-- 这里的css 就是一个辅助函数-->
    </head>
    <body>
            <%- partial('_partial/header') %>
            <!-- 引入其他部分-->
            <main>
                 <%- body %>
                <!-- 这里会将其他页面的内容填充进来组成一个完整的页面 -->
            </main>
    <%- partial('_partial/footer') %>
    </body>
</html>

index页面

显然的,这是我们进入网站看到的第一个页面,一般来说,这个页面要包含最近的文章展示,根据个人喜好,也可以添加标签云、最新评论等。

<section class="posts">
    <% page.posts.each(function (post) { %>
        <article class="post">
            <h1>
                <a class="title" href="<%- url_for(post.path) %>"> 
                    <%= post.title %> 
                </a>
            </h1>
            <%- partial('_partial/meta',{page:post}) %>
            <div class="content">
                <% if(post.excerpt) {%>
                <%- post.excerpt="" %="">
                <%} else{="" %>="" <%-="" post.content="" <%="" }="" <="" div>="" <div="" class=""continue">" <a="" href=""<%-" url_for(post.path)="" %>">="" __('post.continue')="" -%>="" <i="" fa-angle-right"="" aria-hidden=""true"></i>" a>="" article>="" })="" section>="" partial('_partial="" paginator')="" <="" code="">

这个页面看起来要复杂了许多。我们从大到小入手:通过page.posts我们获取到了文章列表,对于其中的post,又有title、tag、category等量。content四获取文章的内容,而excerpt是用来获取摘要。如果在书写markdown 文章时加入<!-- more -->那么在这之前的内容将会被视为摘要,如果你懒得做,也可以使用自动生成摘要的插件。
<%- partial('_partial/meta',{page:post}) %>这段代码中比layout页面中的引入要多出了一个参数,这里是将meta这个页面中的page都赋值为post。主要是用来进行参数传递作用,这里是因为meta不仅在主页要用,在文章页也要使用,而文章页中我们时通过page.tags来取得内容的。
<%- __('Post.Continue') -%>这个写法看起来有些奇怪,它也是一个辅助函数吗?虽然在hexo的文档中没有把它明确为一个辅助函数,但实际上这确实是一个函数,它的作用是进行站点国际化。听起来很高大上吧!不过其实实现很简单,在上述的项目结构中我们还可以增加一个language目录,其中针对每一种语言增加一个yml文件,把需要国际化的量储存在里边,根据整个站点config中的language声明的语言而进行动态调整。比如,我们在en.yml 将Continue声明为continue,在zh-CN.yml中声明为继续阅读。
<%- partial('_partial/paginator') %>这里我们引入了一个paginator组件,它是用来分页的,跟普通的分页不同,hexo中分页的实际工作我们丝毫没有涉及,只需要简单地使用辅助函数就可以:

<%- paginator({
      prev_text: "&laquo; "+  __('Paginator.Prev'),
      next_text: __('Paginator.Next')+" &raquo;"
  }) %>

post页面

有了前面两个页面的基础,这里就不废话了。

<article class="post">
            <h1>
                <a class="title" href="<%- url_for(page.path) %>"> 
                    <%= page.title %> 
                </a>
            </h1>
<%- partial('_partial/meta') %>
<%- partial('_partial/toc') %>
            <div class="content">
                <%- page.content %>
            </div>
<%- partial('_partial/copyright') %>
</article>

archive页面

<section class="archive">
  <ul class="post-archive">
    <% var last = 1997 %>
    <% page.posts.each(function (post) { %>
    <% if (last != post.date.year()){ %>
     <span class="year"> <a><%=post.date.year()%></a></span>
      <% last = post.date.year()%>
    <%}%>
      <li class="post-item">
        <span class="date"><%= date(post.date, "MM-DD") %></span>
        <a class="title" href="<%- url_for(post.path) %>"><%= post.title %></a>
      </li>
    <% }) %>
  </ul>
</section>
<%- partial('_partial/paginator') %>

对这个页面,我们只讲解一下<% var last = 1997 %>,这里参考了hexo自带主题landscape的方案。为了实现按照不同年份归档,先定义一个很小的年份值,如果以后的年份不同于之前的值,那就把last置为它,并且显示一个较大的年份块。

添加样式

这里我们选用的是stylus来书写css,选择它的原因也很简单,因为hexo自带。

这里已经没什么需要额外讲解的了,css总不会也跟其他的有不同吧。只是请注意,为了保证你的主题能够被更多人使用,请务必做好不同浏览器的兼容。类似地,你也应该在配置文件中增加更多的自定义开关,你不能默认认为用户总是想向你提供两个打赏的二维码,所以一个二维码的样式呢?或者索性不想要打赏呢?

提交主题

经历了以上这些,你的主题已经基本具有可用性了,但是如何让更多人知道并且使用你的主题呢?你可以通过在各种技术型社区发帖,也可以发动身边的人使用。更官方的方案,是向hexo提交自己的主题。你具体可以参考这里主题| Hexo。准备好一份截图,fork一份hexojs/site添加自己的主题信息和截图后再向官方提出pull request请求,不必担心,因为他们真的很友善。

not found!