完成一个Hexo的主题其实很简单,和写静态页面差很少,只是内容部分经过Hexo的变量去获取,并且Hexo还内置了一些辅助函数帮你快速方便地完成繁琐的处理。css
在写代码以前要先把项目结构搭建好,一个Hexo主题的项目名就是主题名字自己,项目内的目录结构以下: (生成树形图是用的tree
, mac上直接brew install tree
就能够了,之前不写都不知道囧)html
. ├── _config.yml //记录主题配置信息 ├── layout //存放布局模板文件 │ └── _partial //布局文件中可共用的模板 └── source //静态资源文件夹 ├── css ├── fonts ├── js └── sass
项目结构搞好就能够开始写代码了!由于当初我是仿landscape
写的,并且ejs
也是我以前看nodejs时就接触过的,所以就直接用ejs写模板文件了,样式使用了sass (scss
。node
模板文件在layout
文件夹下,文件名对应Hexo中的模板名,有index
,post
,page
,archive
,category
,tag
几种,对于普通的header + content + footer
的页面结构,header
和footer
每每是能够复用的,所以咱们可使用layout.ejs
进行布局,动态的内容使用body
变量去动态渲染,因此个人layout.ejs
大概长这样:api
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/> <title><%= config.title %></title> <%- css('css/style') %> </head> <body> <%- partial('_partial/header') %> <div class="main"> <%- body %> </div> <%- partial('_partial/footer') %> <%- js('js/index.js') %> </body> </html>
partial
,js
和css
是Hexo提供的辅助函数,后面再说。数组
每个模板文件对应的是一种布局,当你使用hexo new <title>
的时候,其实忽略了一个参数,完整的命令是hexo new [layout] <title>
,这个layout
就决定了文章使用何种方式布局,好比建立一个本身简介的About页面,hexo new page "about"
其实就是使用了page布局。每种布局对应到咱们的模板文件上就是index.ejs(首页)
,post.ejs(文章)
,archive.ejs(归档)
,tag.ejs(标签归档)
,page.ejs(分页)
。sass
若是更直观一点,url和模板的对应关系是这样的:hexo
Url | Description | Layout |
---|---|---|
/ | 首页 | index.ejs |
/yyyy/mm/dd/:title/ | 文章 | post.ejs |
/archives/ | 归档 | archive.ejs |
/tags/:tagname/ | 某个标签的归档 | tag.ejs |
/:else/ | 其余 | page.ejs |
首页通常是一些博文的摘要和一个分页器,经过Hexo的page
变量拿到页面的数据渲染便可,这里咱们不直接在index.ejs
中写HTML结构,新建一个_partial/article.ejs
,将文章数据传给子模板渲染,而后再额外传入一个参数{index: true}
,对后面的post.ejs
和page.ejs
加以区分,让子模板能正确渲染。最后,index.ejs大体是这样的:函数
//index.ejs <% page.posts.each(function(post, index){ %> <%- partial('_partial/article', {index: true, post: post}) %> <% }) %> <div class="pagination"> <%- paginator({ total: Math.ceil(site.posts.length / config.per_page)}) %> </div>
文章模板和首页差很少,只是对应的是一篇具体的文章,因此就把文章传入,再额外传入{index: false}
告诉子模板不要按首页的方式去渲染就行了。就一行代码(由于都在子模板里 XD布局
//post.ejs <%- partial('_partial/article', {index: false, post: page}) %>
我我的对Page模板实际上是有点懵逼的,在我本身的实践中是添加about
(hexo new page "about"
)页面后,访问/about
会走分页布局,实际上这个页面对应的内容是/source/about
里的index.md
,也至关于对文章的渲染,所以我把Page模板也写成了和文章模板同样:post
//page.ejs <%- partial('_partial/article', {index: false, post: page}) %>
前面一共有三处共用了article模板,另外page和post的同样的,因此实际上只有两种状况:主页(index: true
)和非主页(index: false
)。对应的_partial/article.ejs
里只要判断这个值就能够正确渲染了,基本结构以下:
//_partial/article.ejs <% if(index){ %> //index logic... <% }else{ %> //post or page logic... <% } %>
标签归档页内容不多,直接用Hexo的辅助函数list_tags
生成一个标签的列表就ok了:
//tag.ejs <%- list_tags() %>
归档页模板和首页差很少,归档页只须要展现文章标题和最后的分页器就好:
//archive.ejs <div class="archive"> <% var lastyear; %> <% page.posts.each(function(post){ %> <% var year = post.date.year() %> <% if(lastyear !== year){ %> <h4 class="year"><%= year %></h4> <% lastyear = year %> <% } %> <div class="archive_item"> <a class="title" href="<%- url_for(post.path) %>"><%= post.title %></a> <span class="date"><%= post.date.format('YYYY-MM-DD') %></span> </div> <% }) %> <div class="pagination"> <%- paginator({ total: Math.ceil(site.posts.length / config.per_page)}) %> </div> </div>
至此,模板文件就写好了,对于category
模板就放弃了,感受比较鸡肋。。。
其实在模板文件中咱们已经看到了page.post
,site.posts.length
,config.per_page
等等,页面的内容就是根据这些变量获取的,由Hexo提供,拿来直接用,Hexo提供了不少变量,但不是都很经常使用,通常就用到如下变量:
site
: 对应整个网站的变量,通常会用到site.posts.length
制做分页器page
: 对应当前页面的信息,例如我在index.ejs
中使用page.posts
获取了当前页面的全部文章而不是使用site.posts
。config
: 博客的配置信息,博客根目录下的_config.yml
。theme
: 主题的配置信息,对于主题根目录下的_config.yml
。制做一个分页器,咱们须要知道文章的总数和每页展现的文章数,而后经过循环生成每一个link标签,还要根据当前页面判断link标签的active状态,可是在Hexo中这些都不用咱们本身来作了!Hexo提供了paginator
这一辅助函数帮助咱们生成分页器,只须要将文章总数site.posts.length
和每页文章数config.per_page
传入就能够生成了。
list_tags([options])
: 快速生成标签列表js(path/to/js)
, css(path/to/css)
用来载入静态资源,path能够是字符串或数组(载入多个资源),默认会去source
文件夹下去找。partial(path/to/partial)
引用字模板,默认会去layout
文件夹下找。知道了Hexo的渲染方式,咱们就可使用HTML标签+CSS样式个性化咱们的主题了,推荐你们使用CSS预处理语言的一种来写样式,这样就能够经过预处理语言自身的特色让样式更灵活。
评论是很经常使用的功能,不如就直接在咱们的主题里支持了,而后经过配置变量决定是否开启,评论区跟在文章内容下面,对于这种三方的代码块,最好也以partial
的方式提取出来,方便移除或是替换。
//_partial/article.ejs <section class='post-content'> <%- post.content %> </section> //评论部分,post.comments判断是否开启评论,config.duoshuo_shortname 和config.disqus_shortname来判断启用那种评论插件,这里优先判断了多说 <% if(post.comments){ %> <section id="comments"> <% if (config.duoshuo_shortname){ %> <%- partial('_partial/duoshuo') %> <% }else if(config.disqus_shortname){ %> <%- partial('_partial/disqus') %> <% } %> </section> <% } %>
再将多说和Disqus提供的js脚本代码放在_partial/duoshuo.ejs
和_partial/disqus.ejs
下就ok了~
highlight.js提供了多种语言的支持和多种皮肤,用法也很简单,载入文件后调用初始化方法,一切都帮你搞定,对于使用那种皮肤,喜爱因人而异,咱们干脆在主题的配置文件中作成配置项让用户本身选择:
//showonne/_config.yml ...other configs # highlight.js highlight_theme: zenburn
对应的layout.ejs
中:
样式文件经过CDN引入,由于不一样皮肤对应不一样的文件名,因此十分灵活。
当初是对应着landscape
照葫芦画瓢写的,最近回头来发现一些不合理的地方,因此就又改了改,也对应着写了这么一篇总结,接下来准备再把样式划分一下,对于颜色这类样式经过变量的方式提取出来,也变得可配置,能让主题更灵活一些。