阅读 85

「译」创建一个Hexo主题-Part1:首页

在这个系列教程中,你将学习怎么从零开始制作一个 Hexo 主题。我很喜欢 Hexo ,并且每天都在使用,不幸的是,直到今天关于主题制作的文档还是相当稀缺。所以我打算弥补这个空缺。

预先准备

  • Hexo 博客的基础使用。如果你是第一次接触,请前往官网阅读教程
  • 了解 Bootstrap
  • 了解 Javascript 模板引擎(我们将使用 EJS )

项目描述

这个项目旨在制作一个 Hexo 主题并详细了解 Hexo 引擎的工作方式。 我不想在 HTML 和 CSS 上花费太多时间,所以我们将重置下面这个Hexo主题:getbootstrap.com/docs/3.4/ex… 。它是 Bootstrap 文档中的一个标准初始模板样例。 我们将一步步地重用 CSS 、复制粘贴 HTML ,直到最后实现想要的效果。 如果你感到困惑或者只对它的代码感兴趣,请前往 github

项目结构

创建一个新的 hexo 博客

让我们从搭建一个全新的 hexo 博客开始吧

// Create a new folder
mkdir hexo-theme-creation 
cd hexo-theme-creation
// Initialise Hexo
hexo init
复制代码

创建主题文件夹

// Enter the theme folder
cd themes
// bootstrap-blog-hexo is also going to be the name of our theme
mkdir bootstrap-blog-hexo
复制代码

注意:如果你想在 git 中保存主题的话(你也应该这么做),请在/themes/bootstrap-blog-hexo/中初始化 git 。

文件夹结构

这是我们开始工作所需要的文件和文件夹:

|-- layout // .ejs templates 
|-- source // source files (CSS, scripts)
|-- _config.yml
复制代码

创建以下两个文件夹和 _config.yml 文件。

复制 bootstrap blog 资源

bootstrap blog template 复制所有我们需要的资源并放在 source 文件夹里。你可以选择通过浏览器查看资源并复制下来,或者是下载该压缩包,之后解压到 source 文件夹里。

|-- layout 
|-- source 
    |-- bootstrap // Copy the boostrap library files here 
    |-- css // Copy the blog's css file here
    |-- favicon
        |-- favicon.ico // Your choice of favicon
    |-- js // Copy the blog's js file here
|-- _config.yml
复制代码

Hexo 的基本要素

在我们开始写第一个模板文件之前,先来看看 Hexo 博客生成过程的基本要素。

页面类型

我们能够在主题中定义 6 种页面类型,与之相对应地,在 public 文件夹生成的每一个单独的 HTML 页面都属于下面模板中的其中一个:

模板 回退 页面描述
index 这是博客的首页,也是网站的主要入口。本教程中我们将让其显示文章摘要列表
post index 这是文章的详情页。我们将展示一篇完整的文章以及一个评论区
page index 这是页面的详情页,与 post 一样,但是是 page 类型的 post
archive index 这是归档页。它将显示我们博客中所有文章的标题和详情页链接
category archive 这是分类页。与归档页类似,但是会根据类别进行筛选
tag archive 这是标签页。与分类页类似,但是会根据标签进行筛选

在本篇教程中我们将创建 index 模板。

在页面生成过程中, Hexo 将会搜索名字为 index.ejs, post.ejs , page.ejs 等的文件,这些模板之后用于创建静态 HTML 页面。

公共布局

Hexo 支持使用 公共的布局文件 ,上面的模板都将使用到该文件。 该文件命名为 layout.ejs 。不同页面类型的模板会创建一些内容,而这个文件就好比这些内容的“外壳”。 在我们的主题中,公共布局将包括:<html>标签、<head>标签、头部、菜单、底部和侧边栏。基本上是所有类型的页面都具备的元素。 不同的页面模板将只负责创建实际内容,这些内容将放在我们的主体部位。

变量

在所有的模板中,我们都可以使用 hexo 引擎提供的内置变量。以下是部分变量:

  • Site:site包含了网站的信息。例如,我们可以通过site.posts访问博客中的所有文章。当我们想要显示统计数据的时候,这将派上用场。
  • Page:page是主要变量,包含了许多与当前页面相关的信息,包括所有的文章标题、日期、内容等。
  • Config:config是一个指向站点_config.yml文件的 JavaScript 对象
  • Theme:theme是一个指向主题_config.yml文件的 JavaScript 对象

主题的布局创建

上面提及了/layout/layout.ejs文件,现在我们开始来创建它。

顶部标签

首先创建 layout.ejs 文件并插入<html></html>标签

//layout/layout.ejs
<html>
<!-- Head tag -->
<%- partial('_partial/head') %>
</html>
复制代码

这里我们将所有<head>标签里的代码提取出来并放在局部视图中,这有助于实现关注点分离和代码重用。 语法是partial('path' [, arguments]) 在创建layout/_partial/head.ejs文件后,从 bootstrap 源码中复制 head 标签里的代码:

// layout/_partial/head.ejs
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="favicon/favicon.ico">
    <title>Blog Template for Bootstrap</title>
    <!-- Bootstrap core CSS -->
    <%- css('bootstrap/css/bootstrap.min.css') %>
    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <%- css('css/ie10-viewport-bug-workaround.css') %>
    <!-- Custom styles for this template -->
    <%- css('css/blog.css') %>
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>
复制代码

这很简单,我们只需使用 CSS helper 插入样式表。 source 文件夹中的文件将会被复制到站点根目录下,所以不要在路径中包含source/ 我们将让<title><meta>标签保持动态,不过现在先暂且不管它们。

底部标签

底部标签位于</body>之前。我们将在这个局部视图中包含所有脚本。 先修改一下布局:

// layout/layout.ejs

<html>
<!-- Head tag -->
<%- partial('_partial/head') %>
<body>
    <!-- After footer scripts -->
    <%- partial('_partial/after-footer') %>
</body>
</html>
复制代码

然后创建layout/_partial/after-footer.ejs的内容:

// layout/_partial/after-footer.ejs

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<%- js('bootstrap/js/bootstrap.min.js') %>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<%- js('js/ie10-viewport-bug-workaround.js') %>
复制代码

注意 JS helper function 的使用,它将引用本地 js 文件。

顶部菜单

类似地,在<body>标签后创建顶部菜单。

// layout/layout.ejs

// [...]
<body>
    <!-- Menu -->
    <%- partial('_partial/menu') %>
    
// [...]
复制代码

layout/_partial/menu.ejs的内容:

// layout/_partial/menu.ejs

<div class="blog-masthead">
    <div class="container">
        <nav class="blog-nav">
            <% for (var i in theme.menu){ %>
                <a class="blog-nav-item" href="<%- url_for(theme.menu[i]) %>"><%= i %></a>
            <% } %>
        </nav>
    </div>
</div>
复制代码

注意theme全局变量的使用,它指向的是主题的_config.yml文件。为了可以在主题配置中配置菜单,我们需要在_config.yml文件中添加配置:

_config.yml
# Header
menu:
  Home: /
  Archives: /archives
复制代码

menu.ejs中我们遍历了配置文件中所有的菜单项目并创建对应的链接。

顶部

顶部将位于顶部菜单下面,它包含了博客标题和子标题:

// layout/_partial/header.ejs

<div class="blog-header">
    <h1 class="blog-title"><%= config.title %></h1>
    <p class="lead blog-description"><% if (config.subtitle){ %><%= config.subtitle %><% } %></p>
</div>
复制代码

这里我们使用了指向站点_config.yml文件的config变量,它包含了可供配置的标题和子标题属性。 注意在布局的<div class="container"><div>中嵌套顶部:

// layout/layout.ejs

<html>
<!-- Head tag -->
<%- partial('_partial/head') %>
<body>
    <!-- Menu -->
    <%- partial('_partial/menu') %>
    <div class="container">
        <!-- Blog Header: title and subtitle -->
        <%- partial('_partial/header') %>
    </div>
    
// [...]
复制代码

底部

底部现在是完全静态的,内容如下:

// layout/_partial/footer.ejs

<footer class="blog-footer">
    <p>Blog template built for <a href="http://getbootstrap.com">Bootstrap</a> by <a href="https://twitter.com/mdo">@mdo</a>.</p>
    <p>Adapted to Hexo by <a href="http://www.codeblocq.com/">klugjo</a>.</p>
    <p><a href="#">Back to top</a></p>
</footer>
复制代码

主要内容和侧边栏

此时,我们再加上主要内容和侧边栏,基本就差不多了。 下面是最终的layout.ejs

// layout/layout.ejs

<html>
<!-- Head tag -->
<%- partial('_partial/head') %>
<body>
    <!-- Menu -->
    <%- partial('_partial/menu') %>
    <div class="container">
        <!-- Blog Header: title and subtitle -->
        <%- partial('_partial/header') %>
        <div class="row">
            <!-- Main Content -->
            <div class="col-sm-8 blog-main">
                <%- body %>
            </div>
            <!-- Sidebar -->
            <div class="col-sm-3 col-sm-offset-1 blog-sidebar">
                <%- partial('_partial/sidebar') %>
            </div>
        </div>
    </div>
    <!-- Footer -->
    <%- partial('_partial/footer') %>
    <!-- After footer scripts -->
    <%- partial('_partial/after-footer') %>
</body>
</html>
复制代码

body变量对应了不同页面类型模板创建的内容(参见上面)。 至于侧边栏,我们现在暂且使用来自 bootstrap 模板的硬编码:

// layout/_partial/sidebar.ejs

<div class="sidebar-module sidebar-module-inset">
    <h4>About</h4>
    <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
</div>
<div class="sidebar-module">
    <h4>Archives</h4>
    <ol class="list-unstyled">
        <li><a href="#">March 2014</a></li>
        <li><a href="#">February 2014</a></li>
        <li><a href="#">January 2014</a></li>
        <li><a href="#">December 2013</a></li>
        <li><a href="#">November 2013</a></li>
    </ol>
</div>
<div class="sidebar-module">
    <h4>Elsewhere</h4>
    <ol class="list-unstyled">
        <li><a href="#">GitHub</a></li>
        <li><a href="#">Twitter</a></li>
        <li><a href="#">Facebook</a></li>
    </ol>
</div>
复制代码

首页文件

布局到位后,我们就可以开始创建第一个页面类型模板 inde.ejs 了。 这是比较简陋的第一个版本:

// layout/index.ejs
<span>Content</span>
复制代码

别小瞧它,我们可以用这个在浏览器中测试主题:

# Verify that everything is alright
hexo generate
# Start hexo server
hexo server
复制代码

访问 http://localhost:4000/ 。哇! 注意:不要忘记在站点的 config 文件中更新主题:

_config.yml
# Extensions
## Plugins: http://hexo.io/plugins/
## Themes: http://hexo.io/themes/
theme: bootstrap-blog-hexo
复制代码

遍历博客文章

我们想要在首页显示各篇文章的摘要。 首先,在我们的 index.ejs 文件中遍历文章:

// layout.index.ejs

<% page.posts.each(function(item){ %>
    <%- partial('_partial/article-excerpt', {item: item}) %>
<% }); %>
复制代码
  • 通过page.posts获取该页面的所有文章
  • 通过<%- partial('name', args) %>给 partial 传参

文章布局

创建article-excerpt.ejs文件,添加适合主题的代码。这是我的布局:

// layout/_partial/article-excerpt.ejs

<div class="blog-post">
    <!-- Title -->
    <h2 class="blog-post-title">
        <a href="<%- config.root %><%- item.path %>">
            <%- item.title %>
        </a>
    </h2>
    <!-- Date and Author -->
    <p class="blog-post-meta">
        <%= item.date.format(config.date_format) %>
        <% if(item.author) { %>
            by <%- item.author %>
        <% } %>
    </p>
    <!-- Content -->
    <%- item.excerpt || item.content %>
    <!-- Only display the Read More link if we are displaying an excerpt -->
    <% if(item.excerpt) { %>
        <p>
            <a href="<%- config.root %><%- item.path %>">
                <%= theme.excerpt_link %>
            </a>
        </p>
    <% } %>
</div>
复制代码
全文链接

全文链接是由config.root(配置选项,相当于/)和item.path(相对路径或者绝对路径,指向全文)连接组成的。

文章作者

默认情况下, Hexo 没有关于作者属性的的文章变量。不过我们可以在 front matter 中添加任意自己想要的变量。 如果你想要在文章中显示作者名字,那么文章的 front matter 应该类似如下进行设置:

title: Hello World
author: Klughertz Jonathan
---
复制代码
Item excerpt 和 Item content

当用 Hexo 编辑文章时,你可以用<!-- more -->标签从文章内容中截取摘要。在本教程中,因为我们展示的是文章列表,所以选择显示摘要。之后用户可以通过点击文章标题或者“阅读更多”的链接浏览全文。

“阅读更多”文本

别忘了,你需要像我这样在主题的配置文件中添加一个新的属性:

_config.yml
# Read More text
excerpt_link: Read More
复制代码

希望接下来的代码容易理解。现在,我建议你写一些除了默认的 Hello World 之外的文章并享受这个结果。

分页器

在本篇文章中,我们最后需要处理的是首页的分页器。 在index.ejs文件中增加一个分页器的 partial :

// layout/index.ejs

<% page.posts.each(function(item){ %>
    <%- partial('_partial/article-excerpt', {item: item}) %>
<% }); %>
<%- partial('_partial/pagination') %>
复制代码

之后开始创建分页器的内容,layout/_partial/pagination.ejs:

// layout/_partial/pagination.ejs

<nav>
    <ul class="pager">
        <% if (page.prev){ %>
            <li><a href="<%- config.root %><%- page.prev_link %>">Previous</a></li>
        <% } %>
        <% if (page.next){ %>
            <li><a href="<%- config.root %><%- page.next_link %>">Next</a></li>
        <% } %>
    </ul>
</nav>
复制代码
  • page.prev:上一页的页码。如果当前页是第一页,则为0
  • page.next:下一页的页码。如果当前页是最后一页,则为0
  • page.next_linkpage.prev_link 是什么就不用多说了。

如果你没有足够的文章用来查看分页器的工作效果,可以在主配置文件中(per_page 属性)调整每一页的文章数。

这就是今天的内容,在下一篇教程中,我们将完成博客剩下的所有页面。

关注下面的标签,发现更多相似文章
评论