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