如何快速實現 markdown 轉 HTML 文件?
我想要在 Github 上開一個主題部落格,我希望通過 Markdown 語法寫作,然後生成 HTML 並附帶自定義樣式顯示在網頁上。
我找到了 gulp-markdown
這個庫,看起來符合我的需求場景。然而這個庫有一個問題,他只能將 Markdown 語法書寫的文字轉換為 HTML 標籤,但並不能自動新增 doctype
文件宣告,這就意味著生成的 HTML 文件並不合法。
# 標題
我是正文。
會被編譯為
<h1>標題</h1>
<p>我是正文</p>
而不是
<!doctype html> <html> <head> <title>XXX<title> </head> <body> <h1>標題</h1> <p>我是正文</p> </body> </html>
這就很尷尬了,我不知道這個庫的作者為什麼要這麼設計這個庫,我覺得它應該有一個配置引數可以自動新增 HTML 文件宣告等資訊,於是我去查詢其文件(注意,它的文件只能FQ才能看),然而呵呵並沒有。
這就很尷尬了,怎麼辦呢?首先我想到的是,使用其他類似 HTML 模版的庫來組合實現我想要的效果,然而搜了一圈發現,並沒有合適的。因為這類庫都需要首先在 HTML 文件中使用其模版語法標記 HTML 要插入的位置,然後在宣告要插入的內容。可是我們並沒有 HTML 模版,套路不同。
所以這個思路行不通,接下來我想到的方案是:
- 寫一個 Gulp 外掛解決這個問題;
- 這個問題肯定不是我第一次遇見,我先查查別人的解決方案;
懶惰是第一生產力,我當然先選擇方案二,果不其然,全網看下來,貌似只有一個臺灣小哥想到了一個解決方案,大體的思路是使用大陸前端娛樂圈知名人士方方老師寫的一個 gulp-html-extend
庫,實現一種類模版語言的 HTML 混入。為了實現這一點,你需要在每個 Markdown 文件裡寫下模版語言語法,類似這樣:
<!-- @ @master = ../../layout/master.html-->
<!-- @ @block = content-->
# 我是標題
我是正文
<!-- @ @close-->
額,很顯然這個解決方案並不優雅。為什麼我要在每次開心書寫 Markdown 的時候先寫這堆奇怪的註釋啊。我拒絕。
那麼我們又回到了最初的起點,那個殘酷的方案一又縈繞在我心頭,要寫一個 Gulp 外掛嗎?不!我的懶惰不允許我就在這裡放棄!
當你不知所措的時候,不妨回到最初的起點,思考問題的本質是什麼,這通常會激發我的靈感。這次我也這麼做了,果不其然有所收穫。
讓我們一起想想,我實際要做的無非就是把由 gulp-markdown
生成的 HTML 標籤插入到一個這樣的「殼子」裡:
<!doctype html>
<html>
<head>
<title>XXX<title>
</head>
<body>
<!-- 在這裡插入 -->
</body>
</html>
所以我只需要在每個生成的 HTML 檔案的頭尾插入這些字元:
<!doctype html>
<html>
<head>
<title>XXX<title>
</head>
<body>
和這些字元:
</body>
</html>
就行了,所以問題轉變為一個檔案字串拼接的問題,我很快就找到了 gulp-header
這個庫,它能實現我們在每個檔案頭部插入字元的需求,而根據「對稱就是美」定理,當然會有一個 gulp-footer
庫來解決我們後半部分的需求,這樣我們的問題就被完美解決了!
但是,老實講這樣的方案還是不夠優雅,因為我們給檔案前後插入太多字元了,我寫程式碼習慣遵照 make it simple and stupid 的原則,因此對這個方案並不滿意,我希望只插入最少的字元解決這個問題,因此接下來的問題就是:最少能插入多少字元保障 HTML 文件的規範性?
進過一番資料的查詢,我發現,原來 <html>
,<head>
和 <body>
標籤都是可以省略的!只要一個 HTML 文件具備 <!doctype html>
文件宣告和 <title>
標籤,對於現代瀏覽器而言,就是一個合法合規的 HTML 文件!瀏覽器會自動生成 <html>
,<head>
和 <body>
標籤,並且把第一個不能放入 <head>
標籤的標籤和隨後的標籤都插入 <body>
標籤中。沒想到瀏覽器這麼智慧吧,我也沒想到。
現在真相大白,解決這個問題的方案就很簡單了,我們就只用在生成的 HTML 文件中新增最少的字元,這是我的 gulpfile.js
的配置:
const gulp = require('gulp')
const markdown = require('gulp-markdown')
const header = require('gulp-header')
gulp.task('default', () =>
gulp
.src('blog/**/*.md')
.pipe(header('<!doctype html><title>Blog</title>\n\r'))
.pipe(
markdown({
headerIds: false,
}),
)
.pipe(gulp.dest('html')),
)
搞定!
總結
通過這一番調研,我找到了解決 Markdown 生成規範的 HTML 文件的一種比較優雅的解決方案,並在調研過程中學到了關於 HTML 文件必備標籤的相關知識,可謂是獲益匪淺。希望閱讀這篇文章的你也能夠有所收穫。
最後我再囉嗦兩句,可能有人會問,為什麼不搞個瀏覽器同步渲染,所見即所得,同步編輯豈不是更加炫酷,這個其實我有想過,最終決定不搞這個的理由是我覺得既然使用了 Markdown 語法,我的目的就是專心寫作,我並不想因為要看到樣式而分心,因此我在編輯器中只管我要寫什麼,當我寫好 CSS 後,我就對內容如何呈現已經心中有數了,因此我認為「所見即所得」的功能沒什麼用。當然,如果你還是想要新增,相信對你而言也不是什麼難事,畢竟方法之前的那位臺灣小哥已經給出了。