本文已不再於此更新,最新版本請見: xjtu-blacksmith.cn/essay/jekyll-of-pages。
GitHub 與 Gitee 提供的 Pages 服務中,均內嵌了 Jekyll 支持(Gitee 還提供了 Hugo 與 Hexo 支持)。所謂「支持」,即指這些生成工具掛在雲端;你只需要提供原始代碼(如 Markdown 文檔、Sass/Stylus/Less 樣式表),再由 Pages 服務自動編譯、部署即可。這樣,搭建網站的技術門檻進一步下降,你只需要會兩件事就能搭建網站了:
- 會寫 Markdown 文檔;
- 注冊 GitHub 或 Gitee 賬號,點點鼠標,在你的代碼倉庫中啟用 Pages 服務。
因為技術門檻如此之低,導致不少用戶壓根就意識不到 Pages 服務內置了 Jekyll 工具,甚至以為每一個 Markdown 文檔理所當然地就能變成一個網頁。此外,另一個常被忽視的問題是:由 Pages 服務調用的 Jekyll 工具,並非最新版本,而且隱性地增添了許多插件,這可能使用戶在本地使用 Jekyll 或遷移平台時碰上「不協調」的問題。最常見的一個問題就是:在 GitHub Pages 上正常生成的代碼倉庫,到 Gitee Pages 上就變得一團糟。這不是因為 Gitee Pages 的功能「不如」GitHub Pages,而是因為:
GitHub Pages 沒有告訴你它們為自己的 Jekyll 多加了幾個插件,Gitee Pages 也沒有告訴你它們的 Jekyll 並沒有這些插件。
這里對 GitHub Pages 與 Gitee Pages 所使用的 Jekyll 進行一個簡單的分析(后面姑且簡稱為 GitHub Jekyll 與 Gitee Jekyll),以說明它們隱性地附加了哪些功能,需要特別注意。
Jekyll on GitHub Pages
GitHub Pages 中所采用的 Jekyll 及插件、依賴,被匯總到名為 github-pages 的 Gem 中(主頁)。如果你在本地安裝了這個 Gem,可以運行它來查看其所要求的各項依賴版本。以目前最新的 204 版本為例,在 shell 中運行:
$ github-pages versions
+------------------------------+---------+
| Gem | Version |
+------------------------------+---------+
| jekyll | 3.8.5 |
| jekyll-sass-converter | 1.5.2 |
| kramdown | 1.17.0 |
| jekyll-commonmark-ghpages | 0.1.6 |
| liquid | 4.0.3 |
| rouge | 3.13.0 |
| github-pages-health-check | 1.16.1 |
| jekyll-redirect-from | 0.15.0 |
| jekyll-sitemap | 1.4.0 |
| jekyll-feed | 0.13.0 |
| jekyll-gist | 1.5.0 |
| jekyll-paginate | 1.1.0 |
| jekyll-coffeescript | 1.1.1 |
| jekyll-seo-tag | 2.6.1 |
| jekyll-github-metadata | 2.13.0 |
| jekyll-avatar | 0.7.0 |
| jekyll-remote-theme | 0.4.1 |
| jemoji | 0.11.1 |
| jekyll-mentions | 1.5.1 |
| jekyll-relative-links | 0.6.1 |
| jekyll-optional-front-matter | 0.3.2 |
| jekyll-readme-index | 0.3.0 |
| jekyll-default-layout | 0.1.4 |
| jekyll-titles-from-headings | 0.5.3 |
| jekyll-swiss | 1.0.0 |
| minima | 2.5.1 |
| jekyll-theme-primer | 0.5.4 |
| jekyll-theme-architect | 0.1.1 |
| jekyll-theme-cayman | 0.1.1 |
| jekyll-theme-dinky | 0.1.1 |
| jekyll-theme-hacker | 0.1.1 |
| jekyll-theme-leap-day | 0.1.1 |
| jekyll-theme-merlot | 0.1.1 |
| jekyll-theme-midnight | 0.1.1 |
| jekyll-theme-minimal | 0.1.1 |
| jekyll-theme-modernist | 0.1.1 |
| jekyll-theme-slate | 0.1.1 |
| jekyll-theme-tactile | 0.1.1 |
| jekyll-theme-time-machine | 0.1.1 |
+------------------------------+---------+
能看到其列出來一大串的 Gem。通過這個頁面也可以看到 GitHub Pages 上的 Jekyll 版本及相關依賴。
以上這些 Gem,可以大致划分為四類:
- Jekyll 及其依賴,比如 Sass 轉換、Kramdown 引擎、Liquid 模板語言、Rouge 高亮器等等。這些算是常規構件,不可或缺。注意,目前 GitHub Pages 使用的 Jekyll 版本為 3.8.5,而最新版本是 4.0.0,有一個「適當」的延遲。
- 為 GitHub Pages 定制的額外功能,主要有兩個:
jekyll-commonmark-ghpages,在 Commonmark 基礎上改出來的 GFM 引擎(但 Jekyll 仍然默認用 Kramdown);github-pages-health-check,用於檢查域名(DNS 服務)和 GitHub Pages 服務是否正常。 - 若干 Jekyll 插件,基本上都是
jekyll開頭。后面會詳細分析。 - 若干 Jekyll 主題,除了 Jekyll 的默認主題
minima和一個基本主題jekyll-swiss之外,還有 13 個jekyll-theme開頭的,它們就是你在 GitHub Pages 服務里即選即用的 13 個主題。
從以上后三類可以看到,GitHub Jekyll 其實「加持」了很多的輔助件,並不單純。而這樣多的輔助構件,最終營造出了前面所提的「每一個 Markdown 文檔理所當然地可以變成一個網頁」之幻覺。事實上,如果是純粹用 Jekyll 搭建網站,所需要做的工作仍然是不少的。
下面再詳細分析一下 GitHub Jekyll 所采用的插件。
Jekyll 的 Markdown 引擎
在 Maruku 停止更新后,Jekyll 的默認 Markdown 引擎變成了 Kramdown,同樣也是一個用 Ruby 開發的工具。Kramdown 實現了相當多的拓展功能,典型者如 LaTeX 公式、行內屬性標記等,拓展了用 Markdown 實現網頁(HTML)的可能性。
GitHub Jekyll 也是默認用 Kramdown 渲染 Markdown,不過前面看到其也提供了一個 GFM 引擎。在 GitHub 的官方文檔中對此有特別說明,並強調「只有使用后者才能保證網站效果與 GitHub 中(渲染的 Markdown 頁面)的一樣」。仔細想想,有這個需求的用戶應該不在少數。
常規插件
在 GitHub Jekyll 所用插件之中,下面這些是比較常規、常見的(強調者表示默認啟用):
jekyll-sitemap,用於生成站點地圖文件sitemap.xml供搜索引擎抓取;jekyll-feed,用於生成 RSS 訂閱鏈接feed.xml;jekyll-coffeescript,CoffeeScript 轉換器;jekyll-redirect-from,重定向插件,從功能上可以理解為permalink的反面;jekyll-paginate,分頁器;jemoji,表情包;jekyll-avatar,提供了形如{% avatar [username] %}的標簽,用於獲取 GitHub 用戶的頭像;jekyll-remote-theme,使你能夠使用掛在 GitHub 上的 Jekyll 主題;jekyll-gist,提供了形如{% gist xxx %}的標簽,用於在頁面上展示 Gist 的內容;jekyll-mentions,使得 GitHub 上的@用戶功能在網站中得到支持;jekyll-relative-links,能夠將指向 Markdown 文檔的鏈接轉換為指向對應 HTML 頁面的鏈接(有點雞肋)。
其中許多算是 Jekyll 的標配插件,經常被使用。它們更多地是提供了一種可選項,不會對網站的生成效果有太大影響。
靜默增強插件
除了上面所提的基本插件,另外的插件則非常「陰險」,默認啟用,發揮了一些你根本意識不到的功能。包括:
jekyll-seo-tag:定制了{% seo %}這個 Liquid 標簽的功能。SEO 的其他方面不說,網站的<title>元素就是它搞定的(使用了_configs.yml文件中的title和description屬性)。所以在用 GitHub Jekyll 時要想改變網頁標題的格式,就必須要求它停止輸出標題:{% seo title=false %},否則你會以為標題是「無中生有」的。jekyll-github-metadata:用於從 GitHub 獲取元信息,比如項目名稱、作者之類的。它主要是給 GitHub Pages 生成的網站提供一些默認參數,比如上面的 SEO 插件就會使用 GitHub 倉庫的項目名稱、描述作為網站的標題和副標題。在本地用 Jekyll 構建 Pages 上的網站時,十有八九會出現「No GitHub API authentication」的警告,這個鍋也得由它來背(它要用 GitHub API 來獲取這些信息)。jekyll-optional-front-matter:根據 Jekyll 的機制,其只會轉換有 YAML 頭信息(哪怕是空的)的 Markdown 文件,這個插件則取消了這一要求。所以如果你發現用在其他場合使用 Jekyll 時許多 Markdown 文件沒有被轉換,你會意識到這個插件的作用:讓你不用寫頭信息。(另外,這個規則對 Sass 文件不使用,所以你得對自己寫的、放在_sass目錄以外的 Sass 文件至少給一個空的頭信息。)jekyll-readme-index:這個插件使得 Jekyll 在找不到index.html或index.md時,將README.md轉換為index.html作為替代。這個功能的好處在於實現了 GitHub 頁面預覽和網站構建的統一,因為在 GitHub 頁面上README的作用就相當於一般網站的index.html。jekyll-default-layout:幫助你自動給首頁套layout: home、給推送文章套layout: post、給一般頁面套layout: page、實在不行就套layout: default。作用很明顯:讓你不用寫頭信息。jekyll-titles-from-headings:自動將一個沒有指明title的 Markdown 文件之首級標題提取為title。從頁面顯示來說,一個頁面有沒有title其實無關緊要,但需要生成網站導航、文章列表等的時候就必須確保每個頁面都有title。這個功能的作用也很明顯:讓你不用寫頭信息。
以上幾個插件,都是「靜默」生效;其中不少在 Jekyll 中並不默認啟用,但它們在 GitHub Jekyll 中全都是默認啟用的。它們發揮的作用,也許你之前從未意識到,但現在一看即知。
GitHub Pages 主題
GitHub Pages 提供的 13 個基本主題,也被包含在依賴當中,這意味着你不需要安裝就能使用它們。它們在 _configs.yml 中用 theme 屬性啟用(也許這是許多人見過的第一行 YAML 代碼?),這會給初學者造成一種誤解,以為其他的主題也可以這樣通過一行代碼來使用。
事實上,如果要在 GitHub Jekyll 中使用其他主題,有這樣兩種辦法:
- 啟用
jekyll-remote-theme插件,這樣你就可以使用任意一個在 GitHub 上公開的 Jekyll 主題(其他地方的不行); - 把主題下載下來,將對應文件拷貝到指定位置——注意清理之前的主題。(Jekyll 的主題管理不是很靈活,不如 Hugo、Hexo 等工具。)
當然,如果你是在本地生成網站文件后再借 Pages 的服務器發布,方法就比較多了。
總結
經過以上對各個依賴的分析,我們可以發現:GitHub Jekyll 提供了相當多的輔助功能,極大的化簡了網站的生成,而我們甚至還不自知。在不清楚這些背景的情況下,嘗試從 GitHub Pages 服務下遷移出 Jekyll 項目,很有可能會踩坑,比如:
- 為什么這個 Markdown 文件沒有被轉換?(因為你沒有寫頭文件,怪
jekyll-optional-front-matter) - 為什么文章列表里的文章標題都是空的?(因為它們的標題是正文中的
h1標簽,沒有寫到頭信息中的title里,怪jekyll-titles-from-headings) - 為什么這個路徑下的頁面不見了?(因為你原來用的是
README.md轉換成index.html,怪jekyll-readme-index)
當然,問題的可能性不多,一一排除總能解決。所以歸結出一個結論:與其花時間琢磨上面這么多 Gem 的關系,還不如自己去踩踩坑。
坑歸坑,好話也還是要說幾句:如果只是一直用 GitHub Jekyll,以上這些都不算是問題,而算優勢。「Markdown 文件自動變成網頁」這樣的好事,還是人人所欲的;它畢竟能讓許多完全不了解前端技術的人構建一整個網站出來,應該算是大好事。
Jekyll on Gitee Pages
Gitee Pages 究竟用的是綠 Jekyll 還是花 Jekyll,沒有公開信息,只能間接地尋找一些蛛絲馬跡了。
嘗試在一個 Gitee 倉庫中啟用了 Gitee Pages,發現它支持 jekyll-seo-tag,但在生成的 HTML 頁面上赫然顯示該插件版本為 2.3.0。經過檢查,這個版本是 2017 年八月發布的,看來有些年頭了。
但是僅從這一個插件不能推斷出所有信息。為此,我又測試了其他幾個插件,發現:
jekyll-sitemap、jekyll-feed兩個插件都可以正常使用,說明它們算是 Gitee Jekyll 的依賴。- 另外,在
jekyll-feed生成的feed.xml中意外發現了 Jekyll 的版本信息:3.6.2。檢查發現這是 2017 年十月發布的版本。 jekyll-mentions插件竟然可以用,指向 GitHub 的用戶主頁——自己做一個 Gitee 版本的應該也挺容易吧?跟 GitHub 有什么關系?jemoji用不了。- 幾個靜默強化插件中,除了
jekyll-github-metadata未啟用之外,其他的都能正常工作。這造成的后果是,在 Gitee Pages 中必須自己寫baseurl,否則站點的樣式表就找不着了,鏈接也會錯亂。 jekyll-remote-theme居然能用……前提是把baseurl寫對。
根據以上的分析,可以得出以下幾個結論:
- Gitee Jekyll 的版本很舊了,一整套工具可能只是 2017 年末的「最新版」。找到了 Gitee Pages 的上線說明,發布時間是在 2018 年年中,差不太遠(也許前端支持做好之后半年在做后端支持)。
- Gitee Jekyll 跟 GitHub Jekyll 對接不良,遷移或同步的話得增補很多信息,並且不少插件用不了。
- Gitee Jekyll 竟然支持 GitHub 上的遠程主題,但是用起來似乎也存在若干問題,不穩定。
以上三條再歸納為一個最終的結論:(目前的)Gitee Jekyll 不可靠。為了用 Gitee Pages,只有在本地生成網站文件再發布了。和 GitHub Pages 比起來,這無疑抬高了使用門檻;這倒不算什么,重要的是在這種情況下 Gitee Jekyll 形同虛設,不會有什么人去用了。
冷嘲熱諷不能解決問題。剛剛和 Gitee 管理團隊聯系上,表示將來會改進這些問題。持續關注。如果 Gitee Pages 的生態做出來了,肯定是也是一件大好事吧!
