您的位置:首页 > Web前端

Hexo高级教程之主题开发

2016-07-21 17:40 519 查看

引言

有感于hexo高级教程实在太少,当初本人在开发Nova主题时,曾遇到过不少坑,为填这些坑,较为深入地学习了hexo源码,又自学了不少node.js知识,才总算将这些坑基本填完。本着人人为我,我为人人的分享精神,特开一hexo高级教程专题,希望广大hexo爱好者拍砖~

本系列的定位为高级教程,所以要求读者具备以下知识或技能:

前端技术:前端基础知识不用说了,必须要具备的比如HTML,CSS,Javascript,Node.js。如果知识储备不足,推荐去W3C School好好学习。

hexo模板hexo中的layout模板都是使用某个具体的模板引擎写的,模板引擎有
swig
,
ejs
jade
等。layout可以视为MVC模式中的
View
层,用于负责具体页面的展示。

hexo变量hexo内置了不少常用的变量,例如site.posts是站点所有的博客文章, config为hexo博客设置,page为hexo页面对象。hexo变量可以视为MVC模式中的
Model
层,负责给
View
提供要展示的数据。

[辅助函数]:hexo中内置了不少[辅助函数],这些辅助函数可以在模板中直接使用,用于快速地插入要展现的变量内容。辅助函数与MVC中的Controller有点类似,负责数据
Model
的获取以及如何在
View
中展示。

Hexo基础知识:基础知识可以自行度娘或谷歌。PS:个人建议还是看官方文档,有简体中文版本,遗憾的是,官方网站在国内访问有点慢☺。

主题修改

在讲到主题开发之前,不得不讲一下主题修改。目前hexo已有许多成熟的主题。但是未必完全符合博主的要求。灵活性好一些的主题,可能通过修改主题配置可以达到博主的目的,有些则需要修改主题模板或CSS甚至是辅助函数。不过与开发全新的主题相比,工作量还是少了许多。个人建议,如无必要,没有必要开发全新的主题。毕竟博客网站重的是内容,而不是外观。大多数主题,都具备了博客该有的功能,就不必像我如此折腾。当然做为极客的人们则另当别论。

主题配置修改

这部分相对简单,因为主题一般有相关的文档来告诉你如何修改。

以主题Nova为例,Nova主题在菜单配置上,有项导航菜单叫做捐赠墙,捐赠墙是http://www.ieclipse.cn 特有的模块,对于其它博客站点并不适用,那么,只需要将它删除或使用#将其注释即可。这样,它就不会出现在菜单栏中了。

主题风格修改

个人推荐在已有主题样式的基础上,新建一个新的CSS文件,并做为引入样式的最后一个。因为CSS按加载的顺序,如果发现有相同选择器的样式,则后面的CSS规则会合并或覆盖原有的规则。举个例子,原来主题中的链接(a标签)颜色为蓝色(
#00f
),可以重写链接(a标签)的CSS。

原来的css:

a {
color: #00f;
}


追加的css:

a {
color: #f96;
text-decoration: none;
}


color规则会覆盖原来的color规则,而text-decoration则会作为新规则引入。CSS查看器,基本上浏览器都自带此功能。调试相对来说比较简单。

主题模板修改

在此,还是以Nova主题为例,如果站点不考虑国际化,只做单语言站点,则没有必要保留语言选择功能。遗憾的是,想要不显示,则不能通过修改主题配置来实现,需要修改主题的模板文件。Nova主题的导航栏菜单位于
layout/partial/header.swig
中,使用记事本之类的编辑打开它,将

<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{__('page.language')}} <span class="caret"></span></a>
<ul class="dropdown-menu">
{%- for lang in get_langs() %}
<li><a href="{{switch_lang(lang)}}">{{ lang_name(lang) }}</a></li>
{%- endfor %}
</ul>
</li>
</ul>


这一段html删除或注释即可

辅助函数修改

以官方的辅助函数list_archives为例,虽然此函数可以设置class参数,不过它的内部在生成ul和li时,都使用了动态的class,自动给class加了后缀。如下所示:

if (style === 'list') {
result += '<ul class="' + className + '-list">';

for (i = 0, len = data.length; i < len; i++) {
item = data;

result += '<li class="' + className + '-list-item">';

result += '<a class="' + className + '-list-link" href="' + link(item) + '">';


这样css中必须使用.xxx-list作为ul,.xxx-list-item为作li的样式,本着精减html的原则,修改后的代码为:

if (style === 'list'){
result += '<div class="' + className + '">';

for (i = 0, len = data.length; i < len; i++){
item = data[i];

result += '<a class="' + className + '-item" href="' + link(item) + '">';
result += transform ? transform(item.name) : item.name;


使用div和a来简化布局。

主题开发

有了前面的主题修改经验,相信博主们对hexo主题已经有一定的了解了。在这里,我把主题开发分为两种

1. 主题迁移,除了hexo之外,还有许多其它的优秀博客系统,比如Wordpress,它们也有自己的主题。其中不乏一些优秀的主题。hexo中有不少主题就是迁移自其它博客系统的优秀主题。此种方式,可以最大方式的利用成熟主题的布局和样式甚至模板。比如,主页博客文章列表,原有的主题可能是将数据库查询结果集遍历输出为html,而迁移之后的主题,则需要对site.posts遍历并输出为html。

全新开发,全新开发是本文介绍的重点,但是个人并不推荐,除非具备一定的设计能力,它需要从零开始对博客进行设计,比如排版,布局,功能等等。本人开发Nova主题,主要是因为目前的主题+插件,不能解决我github项目文档页的展示问题,其次,也为能够更好更深入地学习前端技术☺。

主题设计

Nova为例,我将博客站点分为3模块

博客文章

与其它主题的博客文章一样,博客文章有:首页、标签、分类、归档、分页等基本功能模块。

在版面上,它是一个2栏布局,主栏显示文章列表或文章详情,侧边栏用于放置窗口小部件或者文章目录。

单页

普通单页也采用主-侧边栏布局,主栏显示文章详情,侧边栏显示文章目录。 对于特别的单页,则使用单独的layout。

项目

项目模块作为Nova主题一大特色,采用三栏布局方式,左侧边栏显示项目导航,主栏显示项目文档内容,右侧边栏则放置文档目录。为处理项目相关的页面,Nova引入一个名为
project
的layout。

主题模板

在使用主题模板之前,先确定一种模板引擎,Nova主题使用的是
swig
模板,这也是hexo默认的渲染模板。

所有的主题模板文件须放在主题
layout
目录下,其中index模板与layout模板必不可少。不然运行会报错。在layout模板,可以将html主体结构写入其中。

<!DOCTYPE html>
<html lang="{{ page.lang }}">
<head>
<meta charset="utf-8">
<title>{{ head_title() }}</title>
<!--设置浏览器兼容模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<!--支持响应式 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 网站关键字,影响SEO -->
<meta name="keywords" content="{{ head_keywords() }}">
<!--网站描述,影响SEO -->
<meta name="description" content="{{ head_description() }}">
<!-- Canonical links -->
<link rel="canonical" href="{{ url }}">
<!--加载全局js或css -->
{{ head_jscss() }}
<!-- RSS -->
{{ feed_tag('atom.xml') }}
</head>
<!--Html主体 -->
<body>
<!-- header -->
{{ partial('partial/header') }}
<!-- main -->
{{ body }}
<!-- footer -->
{{ partial('partial/footer') }}
<!-- fixed action bar -->
{{ partial('partial/fab') }}
<!-- after footer, 第三方脚本放在最后,以免影响网页内容加载 -->
{{ js('js/script.js')}}
{{ partial('partial/baidu_analytics') }}
{{ partial('partial/jiathis_share') }}
</body>
</html>


index模板作为博客首页的渲染页,其实也是属于post模板的一种。除了layout模板外,我将其它的模板都做了归类,跟博客文章相关的,都在post子目录中;跟单页相关的放置在page子目录中;跟项目相关的,都放置在project子目录中。详细介绍,请访问nova layout

下面详细介绍博客文章中的首页和详情页模板

文章首页

首页即文章列表页,主栏主要显示文章列表。文章列表项显示标题,日期,分类,标签,文章摘要等信息及分享,评论等操作项,核心代码如下:

<main>
{%- for post in page.posts %}
<div class="card hoverable">
<div class="card-content">
<h3 class="card-title">
<a href="{{ url_for_lang(post.path) }}" class="article-title">{{ post.title }}</a>
</h3>
<div class="divider"></div>
<div class="section post-header">
<!-- sub element must be span -->
<span class="icon nova-calendar">{{ time_tag(post.date) }}</span>
{{ post_cates(post) }}{{ post_tags(post) }}
</div>
<div class="excerpt">{{ page_excerpt(post) }}</div>
</div>
<div class="divider"></div>
<div class="card-action">
<!--  评论,分享,阅读全文等链接 -->
</div>
</div>
{%- endfor %}
<nav>{{ nova_paginator({total:page.total, class:'pagination'}) }}</nav>
</main>


输出结果预览:

{% raw %}

{%- for post in page.posts %}

{{ post.title }}

{{ time_tag(post.date) }}

{{ post_cates(post) }}

{{ post_tags(post) }}

{{ page_excerpt(post) }}

{{__(‘sns.share’)}}

0 {{__(‘sns.comment’)}}

{{__(‘sns.like’)}}

{{__(‘page.more’)}}

{%- endfor %}

{{ nova_paginator({total:page.total, class:’pagination’}) }}

{% endraw %}

侧边栏,侧边栏主要由窗口小部件组成。如文章分类

<div class="panel panel-primary" id="category">
<div class="panel-heading">
<h3 class="panel-title">{{ _p('category.name') }}</h3>
</div>
<!-- use category tree -->
{{ nova_list_categories(site.categories, {class:'list-group', depth: 10, children_indicator: 'category'}) }}
</div>


页面预览:

{% raw %}

{{ __(‘category.name’) }}

{{ nova_list_categories(site.categories, {class:’list-group’, depth: 10, children_indicator: ‘category’}) }}

{% endraw %}

文章详情页

文章详情页,主栏显示文章内容、评论、上一页和下一页导航。

<article class="article post" itemscope itemtype="http://schema.org/Article">
<header class="article-header">
<div class="page-path"><span class="post-category">{{ page_path(post)}}</span></div>
<div class="divider"></div>
{%- if is_post() %}
<h1 class="article-title" itemprop="name">{{ post.title }}</h1>
{%- else %}
<h1>
<a href="{{ url_for_lang(post.path) }}" class="article-title" itemprop="name">{{ post.title }}</a>
</h1>
{%- endif %}
<div class="post-header">
<span class="icon nova-calendar"><span class="hidden-xs">{{__('page.written_on')}}</span>{{ time_tag(post.date) }}</span>
{{ post_tags(post, {class: 'tag-item-simple'}) }}
<span class="post-share right">
<a href="#share" class="icon nova-share"><span class="hidden-xs">{{__('sns.share')}}</span></a>
<a href="#comment" class="icon nova-bubbles"><span class="hidden-xs">{{__('sns.comment')}}</span></a>
<a href="#like" class="icon nova-heart2-full"><span class="hidden-xs">{{__('sns.like')}}</span></a>
</span>
</div>
<div class="divider"></div>
</header>
<div class="article-content" itemprop="articleBody" id="post-content">
{{ post.content }}
</div>
<footer class="article-footer">
<!--<time class="article-footer-updated" datetime="{{ date_xml(page.updated) }}" itemprop="dateModified">{{ __('page.last_updated', date(page.updated)) }}</time>-->
<!-- JiaThis Button BEGIN -->
<div class="jiathis_style"><a name="share"></a>
<span class="jiathis_txt icon nova-share">{{__('sns.share')}}:</span>
<a class="jiathis_button_tsina">{{__('sns.weibo')}}</a>
<a class="jiathis_button_weixin">{{__('sns.wechat')}}</a>
<a class="jiathis_button_twitter">{{__('sns.twitter')}}</a>
<a class="jiathis_button_copy">{{__('sns.copy')}}</a>
<a class="jiathis_button_ishare">{{__('sns.one')}}</a>
<a href="http://www.jiathis.com/share?uid={{theme.share.jiathis.uid}}" class="jiathis jiathis_txt jiathis_separator jtico jtico_jiathis" target="_blank">{{__('sns.more')}}</a>
<a class="jiathis_counter_style"></a>
<a name="like"></a>
<a class="jiathis_like_qzone"></a>
</div>
<!-- JiaThis Button END -->
</footer>
</article>
<div>
<nav>{{ nova_paginator2({show_name: true}) }}</nav>
{{ partial('../partial/donate') }}
{{ partial('../partial/comment') }}
</div>


其它模板

单页,单页与文章详情页类似。不过没有文章详情页复杂。不做详细介绍。

项目文档,项目文档页须借助hexo-generator-github插件使用。在此也不做详细介绍。

更多的nova layout请点击链接查看。

辅助函数

{% raw %}

在前面的主题模板中,出现了大量的
{{}}
包含的文本,它是swig中调用js的方式。
{{}}
包含的内容可以是hexo变量,如
{{post.title}}
即是输出文章的标题。也可以是辅助函数,如
__('sns.share')
即是输出健值为sns.share的国际化文本,其它一些[Nova中]定义的辅助函数有:

{% endraw %}

- page_title():返回页面标题

- page_excerpt():返回文章摘要

- post_cates():返回指定文章的分类

- post_tags():返回指定文章的标签

page_excerpt()
辅助函数代码:

// get page excerpt
hexo.extend.helper.register('page_excerpt', function(post){
var p = post ? post : this.page;
var excerpt = p.excerpt;
if (!excerpt) {
var pos = p.content.indexOf('</p>');
if (pos > 0){
excerpt = p.content.substring(0, pos + 4);
}
}
return excerpt;
});


post_cates()
辅助函数代码

// insert category of post
hexo.extend.helper.register('post_cates', function(post, options){
var o = options || {};
var _class = o.hasOwnProperty('class') ? o.class : 'category-item';
var icon = o.hasOwnProperty('icon') ? o.icon : 'glyphicon glyphicon-folder-close';
var cats = post.categories;
var _self = this;
var ret = '';
if (cats == null || cats.length == 0) {
return ret;
}
ret += '<span class="post-category">';
ret += '<i class="' + icon + '"></i><span class="hidden-xs">' + _self.__('category.label') + '</span>';
cats.forEach(function(item, i){
ret += '<a class="' + _class + '" href="' + _self.url_for_lang(item.path) + '">' + item.name + '</a>';
});
ret += '</span>';
return ret;
});


layout/post/index.swig
中使用

{{ post_cates(post) }}


将输出以下结果:

分类软件技术Web


点击链接查看更多的Nova辅助函数

注:辅助函数放在主题
scripts
目录下


主题资源

主题当中需要使用到的一些资源有css样式表,js脚本及一些图片资源。资源须放置在主题
source
目录下。在生成时,这些资源会直接复制到
public
根目录下,所以在主题模板中对资源的引用,直接以
/
为前缀+路径加载即可,如下所示:

{{ css('css/bs/nova.css') }}
{{ js('js/script.js')}}


第三方插件

hexo是静态博客,所以像评论、分享等功能,须借助第三方插件才能实现。以评论为例,常用的评论系统有多说,友言,disqus(国外)等。如若需要使用这些第三方插件,可以到对应的官方网站上查看使用说明或集成文档。

建议

主题模板中尽量不要写死可能会变的东西,尽量以主题配置项的方式提供配置。比如最近文章显示几条等。

第三方插件脚本尽量放在之前,以免影响页面的显示。

选择一些比较成熟的前端框架,比如bootstrap以获得更好的兼容性。

支持响应式

参考

hexo: https://hexo.io

Nova: http://www.ieclipse.cn/p/hexo-theme-nova

原文链接: http://www.ieclipse.cn/2016/07/14/Web/Hexo-dev-theme/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  前端 hexo hexo主题