當你看到你用的主題出現在兩個以上的博客的時候,那你就要考慮自己寫一個了。本文的主角是 Hexo ,如果你沒有用過,那可以考慮 Hexo 你的博客了,如果你還沒有寫博客,那你真的 該試試了。根據 阮一峰提出的博客三階段,技術人員早晚會選擇 Github Pages 類似的服務,而 Hexo 絕對是值得嘗試的。
寫這篇文章的目的,當然是希望幫你快速的制作一款主題,將要包含的內容如下:
寫這篇文章的原因,就是因為制作了一個主題Random( 代碼、 Demo),大家可以先看看,下面的代碼,大都來自于這個主題。
制作 Hexo 主題,除了需要了解 HTML / CSS / JavaScript 之外,還需要了解兩個主要的技術,首先一個是模板引擎,Hexo支持主流的模板引擎,像 EJS / Jade / Swig 等,另外一個是 CSS 預處理,如 SASS / LESS / Stylus ,當然,這兩個不用,直接使用 HTML / CSS 也是可以的,只不過可能效率會低一點,在本文中,選擇使用如下兩個:
這篇文章將不介紹它們的使用,請參考它們的文檔。
如果你已經看到了這篇文章,基本上你已經是一個 Hexo 用戶了,但還是簡單的介紹一下 Hexo 的流程:
主題的作用就是在 Hexo 生成文件的時候,提供對應的模板和資源。
Hexo 對主題的基本要求,是需要有如下幾個頁面:
以上這些文件,是 Hexo 在生成 HTML 文件時要用到的,全部放在主題的 layout文件夾中。由于上面這些頁面里,有很多代碼是重復的,比如,HTML文件的 head 部分,頁面的頂部導航,底部版權部分等,為了修改方便,組織簡潔,一般會將可重用的部分提出來,再利用模板引擎來引入。
此外,還有些 JS / CSS / 圖片/ favicon.ico 一類的文件,這類文件并不需要 Hexo 進行轉換,直接就在 HTML 頁面里引用了,所以全部放在主題的 source文件夾中。
現在開始項目之前,我都會搜索一下 yeoman 有沒有庫,生成 Hexo 主題就有 generator-hexo-theme。如果還沒有安裝 yeoman ,那先用 npm 全局安裝。
npm i -g yo
接著安裝生成器的庫:
npm i -g generator-hexo-theme
然后到自己的博客目錄之下,進入到 themes目錄,創建一個用主題名命名的新文件夾,比如 test,進入新文件夾,先設置一下目錄的權限,否則 yeoman 會提示權限不足:
chmod 675 ./
運行這個命令一般需要管理員權限,請根據自己系統的情況加 su/ sudo。接著開始生成代碼:
yo hexo-theme
然后選擇一些基本的配置,比如使用什么模板引擎,使用什么 CSS 預編譯等,這里分別選擇 Swig 和 Stylus。完成之后,主題目錄下就會生成一些如下結構的文件:
├── _config.yml // 主題配置文件├── languages // 多語言文件夾├── layout│ ├── archive.swig // 存檔頁模板│ ├── category.swig // 分類文章列表頁模板│ ├── includes // 各頁面共享的模板│ │ ├── layout.swig // 頁面布局模板,其它的頁面模板都是根據它擴展來的│ │ ├── pagination.swig // 翻頁按鈕模板│ │ └── recent-posts.swig // 文章列表模板│ ├── index.swig // 首頁模板│ ├── page.swig // 頁面詳情頁模板│ ├── post.swig // 文章詳情頁模板│ └── tag.swig // 標簽文章列表頁模板└── source ├── css │ └── theme.styl // 主題自定義 CSS 文件 ├── favicon.ico └── js └── theme.js // 主題 JavaScript 文件
趕緊在 Hexo 的主配置文件中使用新主題,到博客根目錄下找到 _config.yml文件,找到 theme行,修改如下:
theme: test
趕緊 hexo s啟動博客,到瀏覽器看看效果吧。
Hexo 支持多語言顯示,在主題的 languages文件夾中,存放具體的多語言文件,可以是 YML 或者 JSON 文件。再在主配置文件 _config.yml中使用下面的方法來指定具體的使用的配置文件名:
language: zh-CN# 或者多個配置文件language: - zh-CN - en
像下面這樣組織語言文件, languages/en.yml:
archive_title: Archivescategory_title: Categorytag_title: Tag
在模板里,當需要在頁面中顯示文字時,可以使用 Hexo 提供的幫助函數 __()/ _p()來讀取具體的值,如:
{% if is_archive() %} {% set pageTitle = _p('archive_title') %}{% endif %}{{ page_title }}
這樣,主題就可以輕松支持使用不同語言的博客主。
在上面生成的代碼中,所有頁面均使用同一個布局,全部擴展自 includes/layout.swig,在這個文件中,可以看到第 51 行有如下的代碼:
{% block body %}{% endblock %}
在其它的布局文件(除開 includes目錄中的)里,都是使對 includes/layout.swig進行擴展,然后指定 body這個塊的代碼,比如像 index.swig的代碼如下:
{% extends 'includes/layout.swig' %}{% block body %} {% include 'includes/recent-posts.swig' %} {% include 'includes/pagination.swig' %}{% endblock %}
這就相當于是使用 includes/layout.swig里的代碼,并且將 block body替換為那兩行代碼。注意,這個功能, EJS 模板引擎是不支持的。
因此,如果你要不同頁面使用不同的布局,那就需要你在各自的頁面里自定義,或者在單獨的布局文件中定義,再擴展。
主題是供了頁面的布局和樣式,在生成 HTML 文件時,Hexo 會把特定的數據,傳給 swig 模板,然后再由 swig 將數據填充到 HTML 文件之中。這些特定的數據,分為如下幾類。
Hexo 的根目錄中,有個 _config.yml文件,它就是主配置文件,數據組織使用 yml語法,其中的項目,可以在模板中直接使用,比如博客的名字、副標題等等之類。這些數據項組織在 config對象中。可以數字、字符串、對象、數組,例如:
# 字符串title: 不可能不確定# 沒有值permalink_defaults:# 邏輯值auto_spacing: true# 數字since: 2010# 數組skip_render: - "books" - "books/*"# 對象social: GitHub: https://github.com/stiekel Coding.NET: https://coding.net/u/Stiekel Twitter: https://twitter.com/SidCN
完整代碼,請參見 _config.yml。
每個主題,還有單獨的配置文件,用于配置與主題緊密相關的內容,格式與主配置文件一致。只不過變量名為 theme。
具體哪些數據放到主配置文件中,哪些數據放到主題配置文件,自由度其實很高,一般的,推薦與博客中的數據相關的,放主配置文件,如博客的名字、作者、菜單配置等,與主題相關的,放到主題配置文件,比如主題的腳本文件列表、樣式文件列表等。當然,在編寫主題的時候,也可以考慮對于某一個數據,既可以放在主配置文件中,也可以放在主題配置文件中,像這樣:
{% set menu = config.menu || theme.menu %}
要讀取菜單配置時,任意哪個配置文件中有都可以,而且是優先使用主配置文件中的配置。
如果要在模板中使用某個具體的值,比如字符串、數字、邏輯變量或者對象的某個成員,可以在主題的模板文件 swig 中直接使用:
{{ config.title }}
這就相當于把配置文件中的 title輸出到 HTML 中。如果是要遍歷數組或者對象,就要復雜一點:
{% set menu = config.menu || theme.menu %} {% for key in Object.keys(menu) %} {% if menu[key] != '/' %}
Object.keys是取出一個對象的所有索引, for key in是遍歷索引數組,即將對象的所有值生成一組用
組織的鏈接。Hexo 提供了很多專門的變量及函數,用于在編寫主題時使用。請參見 變量列表和 幫助函數列表。這里針對常用的一些功能做對應的介紹。
Hexo 為主題提供了一個變量 site,這個變量包括以下幾個成員:
其中, site.posts與 site.pages兩個結構是相同的,它們各自包括兩個成員,一個 length 為長度,一個 data 為具體的數組,它是個對象,但索引是數組,成員是各個文章的詳情。
site.categories和 site.tags則為兩個對象,成員比較多,但具體的分類和標簽列表,存在索引為 data的成員上,該成員為對象,對象的索引為分類和標簽對應的 id,類似于:
"data":{ "cipap3lwj0001fhpvlubaa0sp":{ "name":"亂七八糟", "_id":"cipap3lwj0001fhpvlubaa0sp" }}
不過實際在編寫主題的時候,很少會直接用到這幾個變量。
這個變量便是 page,這個變量的特點是,在不同的頁面中,它的成員會不一樣。比如,在文章歸檔頁,它就有文章列表,如果在文章詳情頁,它就包含有文章的相關信息。而且在不同的頁面中,就算同一個索引的成員,值也會不一樣,比如 page.posts,在首頁,它是按分頁設置限制過的文章列表,而在存檔頁則是所有文章的列表。
比如,在我們生成的代碼中,首頁里使用 includes/recent-posts.swig( 在線代碼)來顯示文章列表,其主要代碼如下:
{% if site.posts.length > 0 %} {# ... #} {% for postItem in site.posts.toArray() %} {# ... #} {% endfor %}{% endif %}
如果你的博客已經有幾篇文章得話,會發現文章雖然列出來了,但并不是按時間來排列的。所以幾乎沒什么用,但如果使用 page.posts變量替代 site.posts,結果就不一樣了,修改一下試試。
再刷新一下首頁,可以看到,文章只有幾篇,并不是全部文章,且按時間倒序排列。 page的所有成員,請參見 列表。
獲取某個頁面的地址,有很多方法。包括:
我們來看看這幾個值各自有何作用。打開 layout/post.swig,在第4行前插入如下代碼:
{{ path | json }}
{{ url | json }}
{{ page.path | json }}
{{ page.permalink | json }}
然后啟動博客,進入做任意一篇文章,可以在頂部看到三個字符串,類似于:
"2016-06/Material-Design-Float-Action-Button.html""http://chensd.com/2016-06/Material-Design-Float-Action-Button.html""2016-06/Material-Design-Float-Action-Button.html""http://chensd.com/2016-06/Material-Design-Float-Action-Button.html"
鏈接的具體樣式,是主配置文件中 permalink來決定的,這里的配置值為 :year-:month/:title.html。可以看出來, path與 page.path輸出一致, url與 page.permalink一致。而這幾個鏈接都是無法直接使用的。因為 page.path的值是相對路徑,所以除了首頁都是不能直接當鏈接的。而 path.permalink,則是帶有主配置文件中 url值配置的全路徑,也不太好當作站內鏈接直接使用。這時候,就需要 Hexo 提供的幫助函數 url_for()來救場了。
再來加一行:
{{ url_for(page.path) | json }}
輸出為:
"/2016-06/Material-Design-Float-Action-Button.html"
這個結果就比較適合作為站內鏈接了。
Hexo 提供了兩個幫助函數 css()和 js(),傳入路徑數組便可生成對應的 link/ script標簽。路徑數組配置在主題的配置文件中。
由于有些代碼在不同的頁面都是共用的,所以有時候就需要根據不同的頁面,做不同的顯示。比如,一般會把 HTML 的
部分寫到一個單獨的文件里。比如,這里生成的 layout/includes/layout.swig文件里,就需要根據不同的頁面,來生成不同的另外幾個,請參見 字符串處理函數列表。
Hexo 提供了多個 時間處理函數,不過一個 date()也就夠用了,接受兩個參數,第一個為時間值,第二個為格式,模板引用中用法如下:
{% set thisYear = date(Date.now(), 'YYYY') %}
HTML 中用法如下:
{{ date(postItem.date, 'M-D') }}
由于是使用 Moment.js 來顯示的時間,所以直接使用 Moment.js 的 時間格式就行了,常用的如下:
Hexo 提供了幫助函數 tagcloud()來生成標簽云。生成的時候,可以設定標簽云的文字大小范圍、排序、顏色等值。具體請參見 參數列表。
以下是生成的一個標簽云 HTML:
ASP.NET Access Android
這樣便可以通知設置 tag-cloud-tags的樣式來自定義標簽云的外觀。
對于長文章,目錄還是非常實用的,Hexo 也提供了 toc()來實現這一功能,具體請參見 文檔。
生成的 HTML 是個有序列表,結構如下:
- 按鈕的定位
只要使用了 toc()函數,無論當前頁面有幾個標題,都會生成對應的代碼。比如想當目錄少于三個的時候自動隱藏,那就得靠 JavaScript 了。可以通過獲取 ol.toc子成員的數量,來確定其顯示或隱藏,jQuery 代碼如下:
if($("ol.toc").children().length <= 3) { $(".toc").hide();}
這樣當目錄條數少于四條時,便自動隱藏目錄。
上面我們曾經提到,對于一個主題,主要有首頁、存檔頁、標簽文章列表頁、分類文章列表頁、文章詳情頁、頁面詳情頁這幾個頁面。這些頁面的實現中,你會發現有大量的代碼是可以共享的,比如:
所以,對于這部分的代碼,一般都會設計成可復用的代碼段,將這些代碼段文章存放在 includes文件夾中,如果在某個頁面中需要使用,只需要使用模板引擎的 include 功能來包含。比如,我們來看看如何組織一個文章列表中的單個文章的鏈接,這個代碼段會在存檔頁、標簽和分類的文章列表頁等地方用到,創建文件 includes/post-title-item.swig,代碼如下:
{# postItem 為存有一個文章的所有信息的對象 #} {# postItem.title 為文章標題,如果沒有標題,則直接截取文章內容 #} {# strip_html 是將 html 代碼中提取可供普通人閱讀的文字部分 #} {# trim 是去除前后空格 #} {% set postTitle = postItem.title || trim(strip_html(postItem.content)) %} {# 標題最多 80 個字符,超過得話,使用 truncate 來截取 #} {% if postTitle.length < 80 %} {{ postTitle }} {% else %} {{ truncate( postTitle, {length: 80}) }} {% endif %}{# 顯示時間 #} {{ date(postItem.date, 'M-D') }} {% if postItem.categories.length %} {# 組織分類的鏈接列表 #} {{ __('category_title') }} {% set i = 0 %} {% for cat in postItem.categories %} {% if i !== 0 %} {{'/'}} {% endif %} {% set i = i + 1 %} {{ cat.name }} {% endfor %} {% endif %} {# 組織標簽的鏈接列表 #} {% if postItem.tags.length %} {{ __('tag_title') }} {% set i = 0 %} {% for tag in postItem.tags %} {% if i !== 0 %} {{'/'}} {% endif %} {% set i = i + 1 %} {{ tag.name }} {% endfor %} {% endif %} {# 有些在文章的頭部指定了照片,也顯示出來 #} {% if postItem.photos %}
{% set i = 0 %} {% for photo in postItem.photos %} {% set i = i + 1 %} {% if i <= 3 %} {# 這里使用了 fancybox 的一些功能,具體后面了解 #}{% endif %} {% endfor %} {% endif %}
這樣,在需要使用的頁面中,單個文章對象的常量名設置為 postItem便可以組織為相應的結構。比如,在標簽文章列表頁,可以這樣組織:
{# 遍歷所有文章,注意,單個文章的對象為 `postItem` #}{% for postItem in site.posts.toArray() %} {% set isShow = false %} {# 確定一下某一個文章,是否包含當前這個標簽 #} {% for tag in postItem.tags %} {% if tag.name === page.tag %} {% set isShow = true %} {% endif %} {% endfor %} {# 包含就顯示 #} {% if isShow %} {% include 'includes/post-title-item.swig' %} {% endif %}{% endfor %}
以上代碼在 layout/tag.swig中, 在線代碼。
首頁一般會包括一些鏈接和最近的幾篇文章,使用的模板文章為 layout/index.swig。鏈接可以固定的,比如,顯示首頁、存檔頁。也可以從配置文件中讀取鏈接列表,再予以顯示。
如果要顯示最近的幾篇文章的列表,可以使用 page.posts中的文章列表,注意加上翻頁鏈接。
分類列表頁顯示博客里的所有分類,分類文章列表頁顯示某個分類中的文章列表。
Hexo 并沒有專門分類列表頁的模板,那該如何處理呢?一般是寫在頁面模板中,即 layout/page.swig里,然后判斷頁面類型變量 page.type,如果是 categories,則顯示分類列表頁。再在博客里創建一個頁面,指定其 type為 categories。實現方法如下,先來看看 layout/page.swig中的代碼:
{% extends 'includes/layout.swig' %}{% block body %} {% set page_title = page.title %} {# 判斷是否是分類列表頁,如果是,顯示對應內容 #} {% if 'categories' === page.type %}{{ page_title || __('category_title') }} {{ list_categories() }} {# 顯示普通頁面的內容 #} {% else %}
{{ page_title }} {% autoescape false %}{{page.content }}{% endautoescape %} {% endif %}
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com