Hexo 作为一款比较流行的博客框架,其主题也是各式各样,Hexo官网上有很多漂亮的主题直接可以使用,但是莫不如自己设计编写一个自己喜欢的。这篇文章将从零开始制作一个 LandScape 主题。
主题介绍
要想使用一个自定义主题,则只需要在<hexo 文件夹>/themes
下新建一个文件夹,名称随自己喜欢,本文则使以test
为例。如要使用此主题,还需将 hexo 目录下的_config.yml
文件中的theme
属性改为此主题的文件夹,即修改为theme: test
。
一个主题的目录结构如下:
.# |
-
配置文件:主题的配置文件,可以在内部配置一些自定义的属性,在代码内部以
config.xxx
获取 xxx 的属性值,如:config.menu # 获取 配置文件中的 menu 属性值,如果 menu 下有多个二级属性值,则用config.menu[i]表示第i个二级属性值
-
languages 文件夹:内包含各个语言文件,以yml语言形式保存。在代码内部以如下形式自动根据配置的语言选择对应语言文件夹内的内容,如果没有对应的语言,则取
default
文件的内容。__('author.job') # 获取 author 下的 job 的内容
-
layout文件夹:所有的布局相关文件都存放在此文件夹。可以使用 swig 模版,如果要使用 ejs、haml、jade 模版,需要在 hexo 跟文件夹下安装相应的插件(本文使用 ejs 模版):
npm install hexo-renderer-jade --save # jade
npm install hexo-renderer-haml --save # haml
npm install hexo-renderer-ejs --save # ejs -
script 文件夹:JavaScript 脚本文件,在 Hexo 启动时,会载入该文件夹下的 JavaScript 文件。
-
source 文件夹:资源文件夹,存放一些模版以外的源文件,如 css、js、图片、logo 等。文件或文件夹名开投为
_
(下划线)。该文件夹下的文件如果可以被 hexo 渲染,则会在执行
hexo d
或者hexo g
命令是被渲染并存储到public
文件夹,如果不能渲染,则会直接拷贝文件到public
文件夹下。如果有文件跳过渲染,则需要在 hexo 根目录下的
_config.yml
下的skip_render
属性后面添加该文件名,如:skip_render: README.md
整体布局模版
每个主题都需要有一个layout/index.ejs
模版作为入口,index
模板内部可以根据需要添加或 partial 文件等作为博客的首页内容。主题的基础布局为layout/layout.ejs
,所有的布局都继承该模版,其中body
部分根据不同页面进行替换,使用语法为:
<%- body %> |
例如:由 index.ejs
、layout.ejs
内容生成的index.html
内容如下:
- ejs
- ejs
- html
This is a index page. |
运行结果如下:
同时可以在index.ejs
模版内部引入局部布局,如下可以引入一个partial_layout.ejs
的模版内容到index.ejs
:
<%- partial('partial_layout')%> |
partial_layout.ejs 的文件内容如下:
<!-- partial_layout.ejs --> |
运行结果如下:
总体规划
在做自己的主题之前要多整个页面有一个整体的规划,包括页面的各个区域显示内容。在 LandScape主题中,页面布局如下:
- Header区域:页面头部文件,包含 Navigation 区域和 背景图片区域;
- Main Content区域:文章的主要显示区域;
- Sidebar区域:目录、分类、Archiv等栏目;
- Footer区域:显示 Copyright 等信息;
所以对于 layout.ejs
文件简要内容为:
|
每个博客的页面都继承于此模版,所以该模版应该定义为所有页面的对于body
区域,正对不同的页面显示不同的内容:
- Index Page: 显示为文章目录列表;
- Category Page:显示为分类相关列表;
- Tag Page:显示为标签相关列表;
- Archive Page:显示为所有的文章列表;
添加配置文件
在进行页面整体规划的时候,需要根据需要动态的显示不同的内容,例如页面的导航栏的内容等,其内容可以在主体布局代码内部写确定值,但是这样就不能根据不同语言进行修改,也不利于维护。因此这类内容可以添加到_config.yml
文件中,在编写模版的时候可以使用theme.xxx
来获取 xxx
的属性值。例如:
menu: |
如果要添加新的配置项,则只需要按键值对写入配置文件即可。使用时只需要在布局文件中使用循环取出menu
的各项内容进行动态填充。
详细设计
Head 部分
Head 部分是加载网页相关的文件,如title、css、meta相关、link相关等,head.ejs
文件在layout/partial
文件夹下,文件内容如下:
<head> |
Header 部分
Header 区域主要是菜单、搜索框等内容。菜单项是通过主题的配置文件,通过循环语句加载配置文件的菜单选项:
<% for (var i in theme.menu){ %> |
搜索框则是利用 hexo 的辅助函数进行添加:
<%- search_form({button: ''}) %> |
Header 文件内容以及对于的css文件如下: <header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer"><div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="<%- url_for() %>" id="logo"><%= config.title %></a>
</h1>
<% if (theme.subtitle){ %>
<h2 id="subtitle-wrap">
<a href="<%- url_for() %>" id="subtitle"><%= theme.subtitle %></a>
</h2>
<% } %>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<% for (var i in theme.menu){ %>
<a class="main-nav-link" href="<%- url_for(theme.menu[i]) %>"><%= i %></a>
<% } %>
</nav>
<nav id="sub-nav">
<% if (theme.rss){ %>
<a id="nav-rss-link"
class="nav-icon" href="<%- url_for(theme.rss) %>"title="<%= __('rss_feed') %>">
</a>
<% } %>
<a id="nav-search-btn" class="nav-icon" title="<%= __('search') %>"></a>
</nav>
<div id="search-form-wrap">
<%- search_form({button: ''}) %>
</div>
</div>
</div>
</header>
Main Content部分
Main Content部分 是整个页面中的主体部分,其内容在不同页面中显示的内容不一样:
- Index Page: 显示为文章目录列表;
- Category Page:显示为分类相关列表;
- Tag Page:显示为标签相关列表;
- Archive Page:显示为所有的文章列表;
- Post Page:显示文章内容
针对上述不同的页面,在layout
文件夹下有index.ejs
、category.ejs
、tag.ejs
、archive.ejs
、post.ejs
。
以ìndex.ejs
和post.ejs
为例:
ìndex.ejs
模版主要是显示首页文章列表的,hexo 本身提供了很多page
相关的变量,常用的变量及描述如下:
变量 | 描述 |
---|---|
page.title | 页面标题 |
page.date | 页面建立日期(Moment.js 对象) |
page.updated | 页面更新日期(Moment.js 对象) |
page.comments | 留言是否开启 |
page.content | 页面的完整内容 |
page.excerpt | 页面摘要,文章开头到<!-- excerpt --> 标签为止的内容 |
page.more | 除了页面摘要的其余内容,和page.excerpt类似,使用<!-- more --> 标签 |
page.path | 页面网址(不含根路径)。我们通常在主题中使用 url_for(page.path)。 |
page.permalink | 页面的完整网址 |
page.prev | 上一个页面。如果此为第一个页面则为 null。 |
page.next | 下一个页面。如果此为最后一个页面则为 null。 |
post
相关的变量和page
类似,但是多出了三个变量:
变量 | 描述 |
---|---|
page.published | 如果该文章已发布则为True |
page.categories | 该文章的所有分类 |
page.tags | 该文章的所有标签 |
除了上述变量之外,可以在每一篇文章内的Front-matter
区域内(Front-matter具体可参考 Hexo 官方文档)以键值对的方式自定义变量。如:
actions: true |
在模版代码内部使用的时候使用如下的方式:
post.actions |
index.ejs
相关代码如下:
<% page.posts.each(function(post){ %> |
如果文章数量较多,则在 index page中会分页依次显示,每页显示的数量由 hexo 的 config 配置文件决定:
index_generator: |
因此在index.ejs 模版中要添加分页显示:
<% if (page.total > 1){ %> |
post.ejs
内容和index.ejs
内容类似,只是对每一篇文章添加了添加了上一篇、下一篇导航,并且去除了excerpt
的显示,即在article
标签的最后添加如下内容:
<% if (post.prev || post.next){ %> |
Footer部分
Footer区域相对比较简单,只需添加简单的 copyRight 就可以:
<footer id="footer"> |
最后将 css 文件引入到 layout 模版中即可,引入 css 文件使用 hexo 的辅助函数 css():
<%- css('css/style') %> |
最终简单的界面运行如下:
国际化(i18n)
在制作主题的过程中,有些内容需要以不同语言来展示,例如导航栏、鼠标提示等。这些内容需要借助 hexo 的国际化功能去实现。文章所使用的语言由 hexo 的配置文件_config.yml
的language
属性控制。此属性可以设置单个的预设语言,也可以设置多个语言来顺位。
# 单个 |
在主题的language
文件夹中对每种语言的文字进行实现,例如:
-
en.yml
index:
title: Home
add: Add
video:
zero: No videos
one: One video
other: %d videos -
zh-cn.yml
index:
title: 首页
add: 添加
video:
zero: 无视频文件
one: 一个视频文件
other: %d个视频文件
在模版中使用多语言时使用__
或者_p
辅助函数,即可取得翻译后的字符串,前者用于一般使用;而后者用于复数字符串。例如:
<%= __('index.title') %> |
总结
至此,一个简单的 hexo 主题就制作完成了,本文省略了部分css文件和js文件。当然,这只是最简单、最基础的主体,如果要制作自己个性的主题还需要在此基础上添加很多有趣的定制和修改。
感谢阅读,希望对你有所帮助。