如何在 Hugo 中用 Prism.js 提供程式碼色彩標註 #
為什麼不用 Hugo 內建的程式碼標註? #
理由一:不方便 #
Hugo 本身有提供語法標註功能沒錯,他是用 golang
的通用性程式碼標註套件 Chroma 產生,並且提供許多語法支援以及行標註,只是它還需要使用 Hugo 本身提供的短碼(short code
)。所以就功能面而言是相當完善的,但缺點就是不方便。
例如說,當我需要標注一段 JavaScript
程式碼時,我必須在文件內打:
{{< highlight javascript >}}
console.log('hello world');
{{< / highlight >}}
但既然我們都用 markdown
了,更常的撰寫習慣就是:
```javascript
console.log('hello world');
```
理由二:產出的code比較多 #
用 Chroma
產出的標注,是在產出頁面時分析他的語法結構,所以點開他的 HTML
,上面那段的標註程式碼會長這個樣子:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-javascript" data-lang="javascript"><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'hello world'</span>);
</code></pre></div>
但如果是一般的 Markdown
產出的 HTML
就只是:
<pre><code class="language-javascript">
console.log('hello world');
</code></pre>
非常的簡單、非常的直白。
為什麼是選擇 Prism.js
#
我的需求 #
所以從上而言,我的需求是:
- 單純用
Markdown
就好,不用再寫一些short code
之類的語法。 - 不要在產出的時候分析語法結構,在客戶端 (
client side
) 處理就好。 - 可以簡單在
Hugo
中應用。
其他替代品 #
接著在 google
或者 github
上搜尋 syntax highlight
可以找到蠻多的:
選擇 Prism.js
的理由 #
理由一:現在仍持續更新中 #
大概看一下各套件最後的更新時間,會發現仍持續在運作的蠻少的,到今年還有發布更新的僅有 Prism.js
以及 hihglight.js
:
最新發布版本 | 最新發布時間 | |
---|---|---|
Prism.js | v1.16.0 | 2019-3-25 |
highlight.js | v9.15.8 | 2019-5-29 |
SHJS | v0.6 | 2008-12-15 |
SyntaxHighlighter | v3.0.90 | 2016-2-13 |
Rainbow | v2.1.4 | 2018-6-2 |
理由二:支援語言廣泛、樣式選擇多 #
大部分的套件不像 Prism.js
以及 highlight.js
提供非常廣泛的支援。像是 Prism.js
就目前為止,提供 176 種語言支援,8 種樣式風格。highlight.js
則提供 185 種語言支援、89 種風格。反之,像是 Rainbow
目前僅提供 21 種語言支援而已。而這些都是倚賴社群的力量呀!
理由三:預設將會自動執行程式碼標註 #
所以就上述兩點理由看來,Prism.js
與 highlight.js
根本是分庭抗禮、不分上下,但最後讓我選擇 Prism.js
的原因在於, highlight.js
需要再另外呼叫。在 highlight.js
的教學使用中:
The bare minimum for using highlight.js on a web page is linking to the library along with one of the styles and calling
initHighlightingOnLoad
:
也就是除了載入 js
檔案後,還需要加入一行程式碼來執行,這實在有點不方便呀。
反之,Prism.js
則會自動對所有 <code>
屬性進行標註,除非你想要手動標註,才需要在匯入時,增加一個屬性標籤 data-manual
來告訴 Prism.js
要手動標註:
<script src="prism.js" data-manual></script>
該如何使用 Prism.js
#
選擇一:使用本地端檔案 #
先到 Prism.js
網站去依照自己的需求以及選擇樣式,生成 JS 以及 CSS 檔案。目前官網提供的內建樣式有 8 種。如果覺得這些樣式都不喜歡的話,官方還有另外提供一些額外的選擇,可以到 PrismJS/prism-themes
看看有沒有喜歡的。
接著把下載的檔案,放到 <你的hugo部落格資料夾位置>/static
底下。然後更改 parital
檔案,但是更改的檔案以及位置,則要看用的主題而不同。例如,目前本部落格使用的是 hugo-book
,那就是在 <你的hugo部落格資料夾位置>/layouts/partials/docs/inject/
底下新增兩個檔案,一個 head.html
另一個則是 body.html
。接著分別在該兩個檔案新增 HTML
碼:
// 在 head.html
<link rel="stylesheet" href="/<剛下載的CSS檔案名稱>.css"/>
// 在 body.html
<script src="/<剛下載的JS檔案名稱>.js"></script>
例如剛從 Prism.js
下載下來的檔案分別叫做 prism.js
以及 prism.css
,然後把它放在 static
資料夾底下。那檔案結構應該會長這個樣子:
你的Hugo部落格資料夾位置
├── static
│ ├── prism.js
│ └── prism.css
而在 head.html
裡面引用的<link>
應該會是:
<link rel="stylesheet" href="/prism.css"/>
而在 body.html
底下引用的 <script>
則是:
<script src="/prism.js"></script>
這樣就大功告成了。
選擇二:使用CDN載入 #
另外,Prism.js
目前也有提供 CDN
載入,現在 cdnjs.com 以及 jsDelivr 皆可以找到 Prism.js
。
不過相較於把檔案下載下來,目前 CDN
也支援用 prism-autoloader
方式自動幫你載入需要的語言設定檔。不過這方面的設定呢,就目前文件上是找不到,而是藏在 github 中的議題裡面:Website: Added basic usage for CDNs
簡單而言呢,就是除了 CSS
樣式檔載入方法是一樣的,載入 Prism.js
核心庫則有點不一樣。在 Prism.js
改成要載入 components/prism-core
以及 plugins/autoloader/prism-autoloader
兩支程式,然後有先後順序,
此外在載入完 prism-autoloader
這隻程式檔案後,需要加一行自動載入的語言設定檔路徑在哪裡。
以下以 jsDelivr
提供的 [email protected]
版為例:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/plugins/autoloader/prism-autoloader.min.js"></script>
<script>Prism.plugins.autoloader.languages_path='https://cdn.jsdelivr.net/npm/[email protected]/components/';</script>
不過有時候載入 CDN 如果遇到延遲,整個網站可能就會卡住,使用者體驗會非常不好。這時候就可以用 HTML5
提供的 async/defer
來幫助我們。現在網路上對於 async/defer
資料應該蠻多的,在這裡就不再贅述,有興趣者可以參考:
不過相較於 async
會在載入後執行,還是會阻塞網頁,defer
則是留到最後才執行,相較於前者體驗更佳,所以我們可以選擇全部加入 defer
屬性:
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-core.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/plugins/autoloader/prism-autoloader.min.js"></script>
不過這樣會有個問題,最後那個設定怎辦?畢竟它必須等到前面兩個都載入後才能執行,不然就會跳出錯誤:
Uncaught ReferenceError: Prism is not defined at HTMLScriptElement.onload ((index):45)
這時候就可以善用 onload
事件,他會等到所有資源載入後才會觸發,相當適用我們的情境,所以我們就可以在 <script>
裡面新增 onload
事件處理。將原本的:
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/plugins/autoloader/prism-autoloader.min.js"></script>
改成:
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/plugins/autoloader/prism-autoloader.min.js" onload="Prism.plugins.autoloader.languages_path='https://cdn.jsdelivr.net/npm/[email protected]/components/';"></script>
最後來看看效果 #
console.log('hello, prism.js')
在本站上面這句程式碼的色彩應該會長這樣(如果主題色彩沒改的話):
如果沒有的話⋯⋯可能就是發生bug了⋯⋯趕緊來信通知。