前言:
本系列文章翻譯自@Jonathan Klughertz的博客,將會用三篇文章的篇幅詳細講解如何製作一個Hexo主題。
我不是學翻譯出身,若有翻譯錯誤或是不到位之處,請指正。
在這個系列教程中,你將學習怎麼從零開始製作一個Hexo主題。我很喜歡Hexo,並且每天都在使用,不幸的是,直到今天關於主題製作的文檔還是相當稀缺。所以我打算彌補這個空缺。
預先準備
- Hexo博客的基礎使用。如果你是第一次接觸,請前往官網閲讀教程
- 瞭解Bootstrap
- 瞭解Javascript模板引擎(我們將使用EJS)
項目描述
這個項目旨在製作一個Hexo主題並詳細瞭解Hexo引擎的工作方式。
我不想在HTML和CSS上花費太多時間,所以我們將重置下面這個Hexo主題:https://getbootstrap.com/docs... 。它是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文件。
- /layout/ 將包含我們所有的EJS模板
- /source/ 將包含我們所有的資源(CSS文件、外部腳本和庫)
- _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:上一頁的頁碼。如果當前頁是第一頁,則為0page.next:下一頁的頁碼。如果當前頁是最後一頁,則為0page.next_link和page.prev_link是什麼就不用多説了。
如果你沒有足夠的文章用來查看分頁器的工作效果,可以在主配置文件中(per_page屬性)調整每一頁的文章數。
這就是今天的內容,在下一篇教程中,我們將完成博客剩下的所有頁面。