如何編寫Hexo主題


完成一個Hexo的主題其實很簡單,和寫靜態頁面差不多,只是內容部分通過Hexo的變量去獲取,而且Hexo還內置了一些輔助函數幫你快速方便地完成繁瑣的處理。

起步

在寫代碼之前要先把項目結構搭建好,一個Hexo主題的項目名就是主題名字本身,項目內的目錄結構如下: (生成樹形圖是用的tree, mac上直接brew install tree就可以了,以前不寫都不知道囧)

.
├── _config.yml   //記錄主題配置信息
├── layout        //存放布局模板文件
│   └── _partial  //布局文件中可共用的模板
└── source        //靜態資源文件夾
    ├── css
    ├── fonts
    ├── js
    └── sass

項目結構搞好就可以開始寫代碼了!因為當初我是仿landscape寫的,而且ejs也是我之前看nodejs時就接觸過的,因此就直接用ejs寫模板文件了,樣式使用了sass (scss

布局

編寫布局文件(layout.ejs)

模板文件在layout文件夾下,文件名對應Hexo中的模板名,有index,post,page,archive,category,tag幾種,對於普通的header + content + footer的頁面結構,headerfooter往往是可以復用的,因此我們可以使用layout.ejs進行布局,動態的內容使用body變量去動態渲染,所以我的layout.ejs大概長這樣:

<!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,jscss是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(分頁)

如果更直觀一點,url和模板的對應關系是這樣的:

Url Description Layout
/ 首頁 index.ejs
/yyyy/mm/dd/:title/ 文章 post.ejs
/archives/ 歸檔 archive.ejs
/tags/:tagname/ 某個標簽的歸檔 tag.ejs
/:else/ 其他 page.ejs

index.ejs

首頁一般是一些博文的摘要和一個分頁器,通過Hexo的page變量拿到頁面的數據渲染即可,這里我們不直接在index.ejs中寫HTML結構,新建一個_partial/article.ejs,將文章數據傳給子模板渲染,然后再額外傳入一個參數{index: true},對后面的post.ejspage.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>

post.ejs

文章模板和首頁差不多,只是對應的是一篇具體的文章,所以就把文章傳入,再額外傳入{index: false}告訴子模板不要按首頁的方式去渲染就好了。就一行代碼(因為都在子模板里 XD

//post.ejs
<%- partial('_partial/article', {index: false, post: page}) %>

page.ejs

我個人對Page模板其實是有點懵逼的,在我自己的實踐中是添加about(hexo new page "about")頁面后,訪問/about會走分頁布局,實際上這個頁面對應的內容是/source/about里的index.md,也相當於對文章的渲染,因此我把Page模板也寫成了和文章模板一樣:

//page.ejs
<%- partial('_partial/article', {index: false, post: page}) %>

_partial/article.ejs

前面一共有三處共用了article模板,另外page和post的一樣的,所以實際上只有兩種情況:主頁(index: true)和非主頁(index: false)。對應的_partial/article.ejs里只要判斷這個值就可以正確渲染了,基本結構如下:

//_partial/article.ejs
<% if(index){ %>
	//index logic...
<% }else{ %>
	//post or page logic...
<% } %>

tag.ejs

標簽歸檔頁內容很少,直接用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

輔助函數(Helper)

制作一個分頁器,我們需要知道文章的總數和每頁展示的文章數,然后通過循環生成每個link標簽,還要根據當前頁面判斷link標簽的active狀態,但是在Hexo中這些都不用我們自己來做了!Hexo提供了paginator這一輔助函數幫助我們生成分頁器,只需要將文章總數site.posts.length和每頁文章數config.per_page傳入就可以生成了。

其他的Helper:

  • list_tags([options]): 快速生成標簽列表
  • js(path/to/js)css(path/to/css) 用來載入靜態資源,path可以是字符串或數組(載入多個資源),默認會去source文件夾下去找。
  • partial(path/to/partial) 引用字模板,默認會去layout文件夾下找。

樣式

知道了Hexo的渲染方式,我們就可以使用HTML標簽+CSS樣式個性化我們的主題了,推薦大家使用CSS預處理語言的一種來寫樣式,這樣就可以通過預處理語言自身的特點讓樣式更靈活。

其他

添加對多說和Disqus的支持

評論是很常用的功能,不如就直接在我們的主題里支持了,然后通過配置變量決定是否開啟,評論區跟在文章內容下面,對於這種三方的代碼塊,最好也以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提供代碼高亮

highlight.js提供了多種語言的支持和多種皮膚,用法也很簡單,載入文件后調用初始化方法,一切都幫你搞定,對於使用那種皮膚,喜好因人而異,我們干脆在主題的配置文件中做成配置項讓用戶自己選擇:

//showonne/_config.yml
...other configs

# highlight.js
highlight_theme: zenburn

對應的layout.ejs中:

樣式文件通過CDN引入,因為不同皮膚對應不同的文件名,所以十分靈活。

最后

當初是對應着landscape照葫蘆畫瓢寫的,最近回頭來發現一些不合理的地方,所以就又改了改,也對應着寫了這么一篇總結,接下來准備再把樣式划分一下,對於顏色這類樣式通過變量的方式提取出來,也變得可配置,能讓主題更靈活一些。

參考資源

了解輔助函數
模板
Hexo中的變量
Hexo主題列表
Hexo使用多說教程
How to use highlight.js


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM