最開始折騰Hexo的時候感受這東西很神奇,經過他和github搭配就能生成免費的靜態博客,並且還有豐富的主題能夠選擇,當我剛入Hexo的時候默認主題是landscape
,後來又使用過NexT
,是一款很漂亮的主題,可是除此以外,還有不少好看的主題,我很好奇這些主題都是怎麼寫出來的,因而乎就仿照landscape
主題開始研究,寫本身的主題,也就是我本身的博客正在用的主題,項目地址在這裏。css
完成一個Hexo的主題其實很簡單,和寫靜態頁面差很少,只是內容部分經過Hexo的變量去獲取,並且Hexo還內置了一些輔助函數幫你快速方便地完成繁瑣的處理。html
在寫代碼以前要先把項目結構搭建好,一個Hexo主題的項目名就是主題名字自己,項目內的目錄結構以下: (生成樹形圖是用的tree
, mac上直接brew install tree
就能夠了,之前不寫都不知道囧)node
. ├── _config.yml //記錄主題配置信息 ├── layout //存放佈局模板文件 │ └── _partial //佈局文件中可共用的模板 └── source //靜態資源文件夾 ├── css ├── fonts ├── js └── sass
項目結構搞好就能夠開始寫代碼了!由於當初我是仿landscape
寫的,並且ejs
也是我以前看nodejs時就接觸過的,所以就直接用ejs寫模板文件了,樣式使用了sass (scss
。git
模板文件在layout
文件夾下,文件名對應Hexo中的模板名,有index
,post
,page
,archive
,category
,tag
幾種,對於普通的header + content + footer
的頁面結構,header
和footer
每每是能夠複用的,所以咱們可使用layout.ejs
進行佈局,動態的內容使用body
變量去動態渲染,因此個人layout.ejs
大概長這樣:github
<!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提供的輔助函數,後面再說。ajax
每個模板文件對應的是一種佈局,當你使用hexo new <title>
的時候,其實忽略了一個參數,完整的命令是hexo new [layout] <title>
,這個layout
就決定了文章使用何種方式佈局,好比建立一個本身簡介的About頁面,hexo new page "about"
其實就是使用了page佈局。每種佈局對應到咱們的模板文件上就是index.ejs(首頁)
,post.ejs(文章)
,archive.ejs(歸檔)
,tag.ejs(標籤歸檔)
,page.ejs(分頁)
。api
若是更直觀一點,url和模板的對應關係是這樣的:數組
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大體是這樣的:sass
//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}
告訴子模板不要按首頁的方式去渲染就行了。就一行代碼(由於都在子模板裏 XDhexo
//post.ejs <%- partial('_partial/article', {index: false, post: page}) %>
我我的對Page模板實際上是有點懵逼的,在我本身的實踐中是添加about
(hexo new page "about"
)頁面後,訪問/about
會走分頁佈局,實際上這個頁面對應的內容是/source/about
裏的index.md
,也至關於對文章的渲染,所以我把Page模板也寫成了和文章模板同樣:
//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模板的理解錯誤了,tag.ejs做用的是具體tag下的歸檔佈局,所以和archive.ejs差很少:
//tag.ejs <div class="tags"> <h1><%= page.tag %></h1> <% page.posts.each(function(post){ %> <div class="tag_item"> <a class="title" href="<%- url_for(post.path) %>"><%= post.title %></a> <span class="date"><%= post.date.format('YYYY-MM-DD') %></span> </div> <% }) %> </div>
歸檔頁模板和首頁差很少,歸檔頁只須要展現文章標題和最後的分頁器就好:
//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
中:
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/styles/<%= theme.highlight_theme %>.min.css">
樣式文件經過CDN引入,由於不一樣皮膚對應不一樣的文件名,因此十分靈活。
當初是對應着landscape
照葫蘆畫瓢寫的,最近回頭來發現一些不合理的地方,因此就又改了改,也對應着寫了這麼一篇總結,接下來準備再把樣式劃分一下,對於顏色這類樣式經過變量的方式提取出來,也變得可配置,能讓主題更靈活一些。