从零开始制作 Hexo 主题
样式参考网站:https://blankj.com/
图片参考网站:http://www.sitecube.com/signup.php
写在前面
本文将会从零开始开发一个简单的博客主题。样式主要参考 Hexo theme 中的 Noise 主题。
开始之前你需要了解:
模板引擎 ejs官网
CSS预处理器
Hexo 文档
本文使用的模板引擎为 ejs,使用的 CSS 预处理器为 stylus。这也是 hexo 项目预装了的 render 插件,如果想使用其他模板引擎或者其他 CSS 预处理器,可以安装相对应的 render 插件。例如我的 Even 主题使用的是 Swig 与 SCSS。
目录结构
主题目录结构以自带的 landscape 主题为例:
.
├── languages 语言文件,用于国际化
├── layout 页面模板文件
├── scripts Hexo 脚本
└── source 主题资源文件,包括页面样式,脚本,字体等
我们在 themes
中新建 theme-example
文件夹,然后在 theme-demo
中按照 landscape 主题的目录结构新建 languages
,layout
,scripts
与 source
文件夹。
创建布局模板
在 layout
中创建 index.ejs
文件,首页将会使用该布局模板生成 HTML 文件。
layout/index.ejs
:
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<title>Home</title>
</head>
<body>
<h1>Hello Word</h1>
</body>
</html>
修改站点配置文件中的主题配置,使用我们刚刚创建的 theme-example
主题:
# Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: theme-example
运行
hexo clean 清除public静态文件
hexo generate 生成public静态文件
hexo server --debug
以 debug 模式开启 Hexo 本地服务器预览,访问 http://localhost:4000/。
写作
在工作文件夹中执行这条命令来新建一篇文章,title即为文章的标题。
$ hexo new <title>
终端会返回一条信息,告诉你文章源文件存放在哪里:
添加页面导航
现在我们需要在页面中添加导航,由于导航不单单会在首页出现,所以我们在 layout
中创建共用的布局文件 layout.ejs
, 同时创建 _partial/head.ejs
保存 HTML 的 head 以及创建 _partial/header.ejs
文件,编写页面导航部分。
layout/layout.ejs
:
<!DOCTYPE html>
<html>
<%- partial('_partial/head') %>
<body>
<%- partial('_partial/header') %>
<main class="main">
<%- body %>
</main>
</body>
</html>
layout.ejs
文件通过 partial()
函数来包含其他文件,使得我们能够更好的组织代码。详见 Templates | Hexo。
layout/_partial/head.ejs
:
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<title><%= config.title %></title>
</head>
这里使用了 config
变量,该变量包含的是站点配置(即站点根目录下 _config.yml
中的配置)。除此之外,Hexo 还提供了许多变量可在模板中使用,详见 Variables | Hexo。
layout/_partial/header.ejs
:
<header class="header">
<div class="blog-title">
<a href="<%- url_for() %>" class="logo"><%= config.title %></a>
</div>
<nav class="navbar">
<ul class="menu">
<li class="menu-item">
<a href="/" class="menu-item-link">Home</a>
</li>
<li class="menu-item">
<a href="/archives" class="menu-item-link">Archive</a>
</li>
</ul>
</nav>
</header>
接着我们清空 index.ejs
中的内容,并添加 <h2>Hello World</h2>
。在 layout
目录下的 index.ejs
会自动继承 layout.ejs
,并将其中的内容填入 <%- body %>
的位置。我们将得到一个有导航菜单的 Hello World 页面。
添加主题配置文件
实际上我们需要让导航菜单根据我们的需要显示不同的项,上面这种写法不方便修改。所以我们会在主题的配置文件中添加导航菜单的配置。在 thmem-demo
下新建主题的配置文件 _config.yml
,在其中添加需要配置的字段。然后可以通过 theme
这个变量来拿到该配置文件中的配置。
theme-demo/_config.yml
:
menu:
Home: /
Archives: /archives
这样我们就可以在 header.ejs
中使用 theme.menu
获取到导航菜单的设置。将 header.ejs
修改为:
<header class="header">
<div class="blog-title">
<a href="<%- url_for() %>" class="logo"><%= config.title %></a>
</div>
<nav class="navbar">
<ul class="menu">
<% for (name in theme.menu) { %>
<li class="menu-item">
<a href="<%- url_for(theme.menu[name]) %>" class="menu-item-link"><%= name %></a>
</li>
<% } %>
</ul>
</nav>
</header>
当需要在导航中添加链接的时候就可以在配置文件中直接添加,例如添加 Github 的链接:
menu:
Home: /
Archives: /archives
Github: https://github.com/ahonn
除此之外还可以添加其他需要的配置,例如 RSS,评论等等。
添加首页文章列表
接着我们完善首页的模板,使其能够显示文章列表。前面已经说过 Hexo 提供了各种有用的变量,在这里将会使用到 page
这个变量。page
会根据不同的页面拥有不同的属性。具体有什么属性,可以获取到哪些数据可以查看这里。
那么这里我们会使用 page
变量的 posts
属性拿到文章数据的集合。编辑 index.ejs
文件:
<section class="posts">
<% page.posts.each(function (post) { %>
<article class="post">
<div class="post-title">
<a class="post-title-link" href="<%- url_for(post.path) %>"><%= post.title %></a>
</div>
<div class="post-content">
<%- post.content %>
</div>
<div class="post-meta">
<span class="post-time"><%- date(post.date, "YYYY-MM-DD") %></span>
</div>
</article>
<% }) %>
</section>
从 page.posts
中获取单篇文章的数据,并获取文章的标题,内容等数据填充到模板中。处理文章创建时间的时候使用了 date()
函数,这是 Hexo 提供的时间处理的辅助函数。本文中使用到的函数如无特别说明,即为 Hexo 的辅助函数。
文章摘录
由于首页显示文章内容时使用的是 post.content
,即文章的全部内容。所以首页会显示每一篇文章的内容,实际上我们并不想在首页显示那么多内容,只想显示文章的摘录。
Hexo 提供了 excerpt
属性来获取文章的摘录部分,不过这里需要在文章中添加一个 <!-- more -->
标记。添加了这个标记之后,post.excerpt
将会获取到标记之前的内容。如果没有这个标记,那么 post.excerpt
会是空的。所以我们可以把首页文章内容部分的 post.content
替换成 post.excerpt
。
<div class="post-content">
<%- post.excerpt %>
</div>
添加页面样式
到目前为止,我们完成了首页的页面结构,但是并没有添加样式,所以看起来很丑。我们在 source
文件中创建一个 css
文件夹来存放样式文件。
由于 Hexo 在新建项目的时候会安装 hexo-renderer-stylus
这个插件,所以我们无需其他步骤,只需要将样式文件放到 css
文件夹中。Hexo 在生成页面的时候会将 source
中的所有文件复制到生成的 public
文件中,并且在此之前会编译 styl 为 css 文件。
在 css
文件夹中创建 style.styl
,编写一些基础的样式,并把所有样式 import
到这个文件。所以最终编译之后只会有 style.css
一个文件。创建 _partial/header.styl
与 _partial/post.style
存放页面导航以及文章的样式,并且在 style.styl
中 import
这两个文件。
_partial/header.styl
:
.header {
margin-top: 2em
display: flex
align-items: baseline
justify-content: space-between
.blog-title .logo {
color: #AAA;
font-size: 2em;
font-family: "Comic Sans MS",cursive,LiSu,sans-serif;
text-decoration: none;
}
.menu {
margin: 0;
padding: 0;
.menu-item {
display: inline-block;
margin-right: 10px;
}
.menu-item-link {
color: #AAA;
text-decoration: none;
&:hover {
color: #368CCB;
}
}
}
}
_partial/post.style
:
.post {
margin: 1em auto;
padding: 30px 50px;
background-color: #fff;
border: 1px solid #ddd;
box-shadow: 0 0 2px #ddd;
}
.posts {
.post:first-child {
margin-top: 0;
}
.post-title {
font-size: 1.5em;
.post-title-link {
color: #368CCB;
text-decoration: none;
}
}
.post-content {
a {
color: #368CCB;
text-decoration: none;
}
}
.post-meta {
color: #BABABA;
}
}
style.styl
:
body {
background-color: #F2F2F2;
font-size: 1.25rem;
line-height: 1.5;
}
.container {
max-width: 960px;
margin: 0 auto;
}
@import "_partial/header";
@import "_partial/post";
最后,我们需要把样式添加到页面中,这里使用了另外一个辅助函数 css()
:
layout/_partial/head.ejs
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<title><%= config.title %></title>
<%- css('css/style.css') %>
</head>
至此,我们会看到站点的首页是这个样子的:
添加分页
在站点的 source/_post/
目录下存放的是我们的文章,现在我们把原本的 hello-world.md
复制黏贴 10+ 次,再查看站点首页。会发现,首页只显示了 10 篇文章。
首页显示的文章数量我们可以通过站点配置文件中的 per_page
字段来修改,但是我们不可能把所有文章都放在一页,所以我们现在来添加文章列表的分页。
新建 _partial/paginator.ejs
:
<% if (page.total > 1){ %>
<nav class="page-nav">
<%- paginator({
prev_text: "« Prev",
next_text: "Next »"
}) %>
</nav>
<% } %>
在 index.ejs
中添加这个文件的内容:
...
</section>
<%- partial('_partial/paginator') %>
这里我们使用到了另外的一个辅助函数 paginator
,它能够帮助我们插入分页链接。
添加文章详情页
文章详情页对应的布局文件是 post.ejs
,新建 post.ejs
:
<article class="post">
<div class="post-title">
<h2 class="title"><%= page.title %></h2>
</div>
<div class="post-meta">
<span class="post-time"><%- date(page.date, "YYYY-MM-DD") %></span>
</div>
<div class="post-content">
<%- page.content %>
</div>
</article>
由于这里是文章的模板,所以变量 page
表示的是文章的数据,而不是首页的文章数据集合。
添加归档页
创建归档页使用的模板文件 archive.ejs
:
<section class="archive">
<ul class="post-archive">
<% page.posts.each(function (post) { %>
<li class="post-item">
<span class="post-date"><%= date(post.date, "YYYY-MM-DD") %></span>
<a class="post-title" href="<%- url_for(post.path) %>"><%= post.title %></a>
</li>
<% }) %>
</ul>
</section>
<%- partial('_partial/paginator') %>
其实结构跟首页差不多,只是不显示文章内容而已。添加归档页的样式:
css/_partial/archive.styl
:
.archive {
margin: 1em auto;
padding: 30px 50px;
background-color: #fff;
border: 1px solid #ddd;
box-shadow: 0 0 2px #ddd;
.post-archive {
list-style: none;
padding: 0;
.post-item {
margin: 5px 0;
.post-date {
display: inline-block;
margin-right: 10px;
color: #BABABA;
}
.post-title {
color: #368CCB;
text-decoration: none;
}
}
}
}
国际化
还记得我们一开始创建的 languages
文件夹吗?没错,它是用来添加多种语言,用于 i18n 的。站点的语言设置为站点配置文件中的 language
。
当该字段为空时,默认使用的是 languages/default.yml
这个文件。那么现在我们来添加这个文件,我们决定主题的默认语言是英文:
Menu:
Home: Home
Archives: Archives
Github: Github
Paginator:
Prev: Prev
Next: Next
目前我们需要主题根据选择的语言自动修改的有上面这些,接着我们需要修改 header.ejs
与 paginator.ejs
这两个文件:
_partial/header.ejs
<header class="header">
<div class="blog-title">
<a href="<%- url_for() %>" class="logo"><%= config.title %></a>
</div>
<nav class="navbar">
<ul class="menu">
<% for (name in theme.menu) { %>
<li class="menu-item">
<a href="<%- url_for(theme.menu[name]) %>" class="menu-item-link"><%- __('Menu.' + name) %></a>
</li>
<% } %>
</ul>
</nav>
</header>
_partial/paginator.ejs
:
<% if (page.total > 1){ %>
<nav class="page-nav">
<%- paginator({
prev_text: "«" + __('Paginator.Prev'),
next_text: __('Paginator.Next') + "»"
}) %>
</nav>
<% } %>
修改之后其实与之前相比没有什么变化,起码看起来是。现在我们添加一个中文的文件:
languages/zh-CN.yml
Menu:
Home: 首页
Archives: 归档
Github: 交友
Paginator:
Prev: 上一页
Next: 下一页
然后我们将站点配置文件中的 language
字段修改为 zh-CN
(与 zh-CN.yml
文件名相同)。再次访问站点之后就会发现导航与分页部分的文字变成了中文。
hexo函数
最后总结
如果你有耐心看我废话了这么多的话,恭喜你,你应该对怎么去写一个 Hexo 主题有了一定的了解。其实说白了,Hexo 就是把那些 Markdown 文件按照不同的布局模板,填上对应的数据生成 HTML 页面,复制 source
中的到生成的 public
文件夹中,中间过程会把需要编译的 stylus/less/sass 等文件编译。
本文并没有提及有关页面 JavaScript 的部分,实际上与写 CSS 样式相同。在 source/js
中写 JavaScript 脚本,然后在模板中引入即可。
感谢阅读,希望对你有所帮助。
从零开始制作 Hexo 主题的更多相关文章
- WordPress 主题教程:从零开始制作 WordPress 主题
为什么要开发WordPress主题? WordPress主题由一系列文件和样式表单组成,这些文件和样式表单共同作用生成WordPress网站的外观.每个主题都不同,用户可以通过这些主题随心所欲地更换自 ...
- 从0开始的Hexo主题制作
从0开始的Hexo主题制作 从零开始制作 Hexo 主题 H2O主题 先坑着
- 如何编写Hexo主题
完成一个Hexo的主题其实很简单,和写静态页面差不多,只是内容部分通过Hexo的变量去获取,而且Hexo还内置了一些辅助函数帮你快速方便地完成繁琐的处理. 起步 在写代码之前要先把项目结构搭建好,一个 ...
- Hexo主题开发
序章 想要一个自己的知识管理系统,用了 Hexo ,但是没有发现自己心仪的主题,就自己做了一个.本文记录了制作的全过程.本人编码功底和前端知识并不是特别雄厚,希望能由此文引出各路大神的兴趣,以后制作出 ...
- 从零开始的 Hexo 生活(一)入门安装篇
目录 前言 一.Hexo 是什么 1.什么是静态网站 2.为什么选择静态网站 3.为什么选择 Hexo 二.Markdown 是什么 1.为什么要学 Markdown 2.怎么学 Markdown 三 ...
- 从零开始制作Minecraft启动器(C++开源)
从零开始制作Minecraft启动器(C++开源) 新手飙车了~~~,MC启动器源码大放送,随心所欲打造自己的专属MC启动器,这不是易语言,是C++...分析原理,关键源码都有详细的注释,代码编好就打 ...
- Github Pages 搭建HEXO主题个人博客
跌跌撞撞,总算是建立起来了.回首走过的这么多坑,也真的是蛮不容易的.那么就写点东西,记录我是怎么搭建的吧. 准备工作 安装Node.js: 用于生成静态页面,我们需要到官网上去下载即可.http:// ...
- 推荐5款简洁美观的Hexo主题
2018-11-17 17:15:46 原文地址:http://www.izhongxia.com 以下是 <hexo 主题列表> 中挑选出来一些比较简洁美观的主题(存在个人主观意识,请勿 ...
- hexo主题中添加相册功能
博客已迁移至http://lwzhang.github.io. 基本上所有的hexo主题默认都没有实现相册功能,一方面相册功能的需求较少,毕竟hexo主要是写博客用的:另一方面实现相册功能比较麻烦,比 ...
随机推荐
- 《SQL CookBook 》笔记-准备工作
目录 准备 1.建立员工表--EMP 2.建立部门表--DEPT 3.EMP表和DEPT表插入数据 4.建立透视表T1,并插入数据 5.建立透视表T10,并插入数据 第二章 shanzm 准备 1.建 ...
- windows组策略实验-本地组策略和域控组策略
windows组策略实验-本地组策略和域控组策略 本地组策略只对本地计算机有效,域策略是计算机加入域环境后对加入域的一组计算机.用户定义的策略,便于管理 本地组策略: 一.实验环境 Windows 7 ...
- JVM之垃圾收集器
前一篇讲了垃圾收集算法--JVM之GC算法.垃圾收集算法——标记-清除算法.复制算法.标记-整理算法.分代收集算法,如果把它看作是方法论,那么下面说的就应该是内存回收的具体实现. 先看一下JVM中有哪 ...
- 深入理解 call,apply 和 bind
在JavaScript 中,call.apply 和 bind 是 Function 对象自带的三个方法,这三个方法的主要作用是改变函数中的 this 指向,从而可以达到`接花移木`的效果.本文将对这 ...
- 【RL-TCPnet网络教程】第38章 TFTP简单文件传输基础知识
第38章 TFTP简单文件传输基础知识 本章节为大家讲解TFTP(Trivial File Transfer Protocol,简单文件传输协议)的基础知识,方便后面章节的实战操作. (本章 ...
- 微信小程序picker组件关于objectArray数据类型绑定
一.前言: 我发现很多的同学都在抱怨说微信小程序的picker的mode = selector/mode = multiSelector 无法实现Object Array数据类型的绑定,其实很多人就想 ...
- 开发人员的必备工具Git(初级)
Git是什么 Git是目前世界上最先进的分布式版本控制系统. 这个软件用起来就应该像这个样子,能记录每次文件的改动: 举个栗子 : 版本 用户 说明 日期 1 张三 删除了软件服务条款5 ...
- Android Studio配置统一管理依赖版本号引用
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本Demo采用的是其中一个方案,其他方案请阅读参考资料<Android Studio中统一管理版本号引用配置> 使用步骤 ...
- java编程行业微信群,无论新手老手欢迎加入,会一直更新
纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群
- 如何让vue自定义组件可以包裹内容,并且渲染出来,以及组件的组合使用
当我们用vue一开始写项目时,按需求文档自定义了一个公用组件,这个组件很多地方都用到了,然后随着项目的推进,又有了新的需求要在里面加东西,但又不是所有的地方都要加 这时候我们想这样往里面塞内容↓ ...