最近在研究Backbone.js和Underscore.js,也基于此編寫(xiě)了一下腳手架,需要寫(xiě)一個(gè)技術(shù)說(shuō)明文檔,風(fēng)格當(dāng)然也要類(lèi)似咯。

首先提煉一下需求
- 模塊化編寫(xiě)文檔,每個(gè)小節(jié)都是一個(gè)單獨(dú)的Markdown文件,便于修改維護(hù)
- 使用gulp進(jìn)行打包編譯,生成一個(gè)完整的HTML文件(單頁(yè)面模式),便于分享轉(zhuǎn)發(fā)
- 最終的展示效果和上圖類(lèi)似,即有導(dǎo)航欄和正文,字體風(fēng)格就直接照搬
需求完成了,接下了就是實(shí)現(xiàn)了。首先就是設(shè)計(jì)目錄結(jié)構(gòu),和標(biāo)準(zhǔn)的gulp項(xiàng)目類(lèi)似,我的目錄結(jié)構(gòu)如下所示。

其中build和dist目錄分別用于存放編譯的中間文件和最終的輸出文件,src目錄下存放的是文檔模塊。
打包過(guò)程分為四個(gè)步驟
第一步 合并md文件,并生成TOC
這里用到了gitdown插件,具體代碼如下
// build whole markdown file
gulp.task('gitdown', function() {
return Gitdown
.readFile(abs(src_path + 'template.md'))
.writeFile(abs(build_path + 'all.md'));
});
這步的作用是根據(jù)預(yù)約定義好的模版生成完整的md文件。
這里需要說(shuō)明的是由于文檔模塊的編排需要手工設(shè)置順序,因此采用模版手工填寫(xiě)各個(gè)模塊的方式,雖然有點(diǎn)麻煩,但可控性比較好,而且所有相關(guān)的模塊信息只出現(xiàn)在template.md文件中,因此也算合理。
模版的TOC部分
[Getting Started](#getting-started)
{"gitdown": "contents", "maxDepth": 2, "rootId": "getting-started"}
[Events](#events)
{"gitdown": "contents", "maxDepth": 2, "rootId": "events"}
模版的Body部分
# Getting Started
{"gitdown": "include", "file": "./model/01Getting-started/first.md"}
{"gitdown": "include", "file": "./model/01Getting-started/second.md"}
# Events
{"gitdown": "include", "file": "./model/02Events/click.md"}
第二步 分割md文件為兩個(gè)部分:導(dǎo)航欄/正文
這里用到了gulp-split-files插件,具體代碼如下
// split whole markdown file into TOC and BODY
gulp.task("split", ['gitdown'], function () {
return gulp.src(build_path + "all.md")
.pipe(splitFiles())
.pipe(gulp.dest(build_path));
});
這步的作用很明顯,就是為了將TOC和Body分開(kāi),以便后續(xù)采用不同的方式進(jìn)行render,并且合并到最終HTML模版到不同位置去。這里同樣需要在template.md文件中指定切分的位置。
/*splitfilename=toc.md*/
...TOC...
/*split*/
/*splitfilename=body.md*/
...BODY...
第三步 將md文件轉(zhuǎn)換為HTML
這步用到的插件是gulp-markdown
TOC的Render
// TOC renderer
var renderer = new markdown.marked.Renderer();
renderer.list = function(body, ordered, start) {
var type = ordered ? 'ol' : 'ul',
startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
var sectionstart = '<div class="searchable_section">\n';
var sectionend = '</div>\n';
return sectionstart + '<' + type + startatt + ' class="toc_section">\n' + body + '</' + type + '>\n' + sectionend;
};
renderer.listitem = function (text) {
return '<li>- ' + text + '</li>\n';
};
gulp.task('md2html:toc', ['split'], function() {
return gulp.src(build_path + 'toc.md')
.pipe(markdown({
renderer: renderer
}))
.pipe(rename({ extname: '.html' }))
// .pipe(concat('markdown.html'))
.pipe(gulp.dest(build_path));
});
Body的Render
// Body render
gulp.task('md2html:body', ['split'], function() {
return gulp.src(build_path + 'body.md')
.pipe(markdown())
.pipe(rename({ extname: '.html' }))
.pipe(gulp.dest(build_path));
});
第四步 合并成最終的HTML
這里用的插件是gulp-file-include,模版文件為src/index.html。
// Merge split html into single one
gulp.task('mergeHtml', ['md2html:toc', 'md2html:body'], function() {
return gulp.src(src_path + 'index.html')//主文件
.pipe(fileInclude({
prefix: '@@',//變量前綴 @@include
basepath: './src/include',//引用文件路徑
indent: true//保留文件的縮進(jìn)
}))
.pipe(gulp.dest(dist_path));//輸出文件路徑
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="viewport" content="width=device-width">
<title>AWF</title>
@@include('./css.html')
</head>
<body>
<div id="sidebar" class="interface">
<a class="toc_title" href="#">
AWF <span class="version">(2.1.0)</span>
</a>
@@include('../../build/toc.html')
</div>
<div class="container">
@@include('../../build/body.html')
</div>
</body>
</html>
回顧一下用到的插件
const gulp = require('gulp');
const abs = require("abs");
const rename = require('gulp-rename');
const markdown = require('gulp-markdown');
const fileInclude = require('gulp-file-include');
const Gitdown = require('gitdown');
const splitFiles = require("gulp-split-files");
現(xiàn)在可以執(zhí)行gulp來(lái)進(jìn)行自動(dòng)化打包編譯了,最終效果如下

是不是學(xué)得很像啊:)
最后給一個(gè)github的地址,本文的示例代碼在http://github.com/SagittariusZhu/gulp-docwrite-scaffold可以找到。