如何快速实现 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 后,我就对内容如何呈现已经心中有数了,因此我认为「所见即所得」的功能没什么用。当然,如果你还是想要添加,相信对你而言也不是什么难事,毕竟方法之前的那位台湾小哥已经给出了。