Jsdoc是一款JS文档的生成工具,提供了丰富的标签,写起文档来十分方便,具体标签用法参考 Jsdoc,这里就不多赘述。但它的用处,远远不止于生成文档,最近通过研究,发现利用Jsdoc工具,可以干更多的事情,待下面剖析。
生成原理
首先来谈谈Jsdoc的生成原理,即如何从注释代码生成最后的网页。这个过程其实很简单,通过文档解析器parser
对每个js文件进行分析,提取出文档信息,之后的一步很关键,Jsdoc将提取出来的信息存放到了taffydb中,进而根据这些信息来生成网页。
为了更好的解释这个过程,来看Jsdoc的源码
1 | /* templates/default/publish.js */ |
生成html的代码逻辑,就在publish
这关键函数中,从接受的参数来看,taffyData
中已经存放好解析之后的文档信息,opts
是配置文件的解析,tutorials
是教程的信息,未做过多研究。那么taffyData中存放的信息试什么样子的呢?来看如下
1 | ***** Debug ****** |
以上信息就是taffyData中存放的文档信息,可以看到,关键的Jsdoc信息,比如@since 1
解析成了since:'1'
,@public
解析成了access:public
,kind:'function'
,经过这般解析,taffyData中的数据已经足够可以用来生成网页,从而可以看出,在publish
函数中,围绕着taffydb的数据,通过各种数据处理,可以做一些更多的事情,这里只是抛砖引玉,比如统计代码的各种信息、检查Jsdoc是否符合规范等等
以一张图简要描述下Jsdoc的原理

至此,我们分析出了Jsdoc的关键信息:Jsdoc解析之后的信息全部存放到taffyData中,而这些信息作为接口publish的参数,提供了良好的扩展性
如果进一步了解信息转储,请查阅:app.js # app.jsdoc.parser.parse -> parser.js # parser.createParser
这个方法
自定义扩展
接下来来看看Jsdoc提供的三种自定义扩展方式,分别是:自定义EventHandler
级别的插件,自定义标签,自定义抽象语法树访问器,下面来分别解释下这三种扩展方式的区别
自定义EventHandler级别的插件
这种扩展方式是最高级别的扩展,如果学过监听器设计模式,看如下代码就一目了然了,一个
EventHandler
插件包含了以下几个阶段的监听1
2
3
4
5
6
7
8
9
10
11exports.handlers = {
parseBegin: function(sourcefiles){},
fileBegin: function(filename){},
beforeParse: function(filename,content){},
JsdocCommentFound: function(filename, comment, lineno){},
symbolFound: function(..params){ },
newDoclet: function(doclet) {},
fileComplete: function(filename,content){},
parseComplete: function(sourcefiles, doclets){},
processingComplete: function(doclets){}
};不难理解,Jsdoc在生成过程中,抛出了以上这些阶段事件供扩展,每个事件的不同参数,代表的当前阶段产生的中间结果。比如在解析之前(parseBegin),传入的参数代表源文件文件名列表,可以做一些正比表达式的匹配,指定生成某些文件的文档。在解析完一个文件的Jsdoc信息时(newDoclet),doclet中就包括了上面taffydb中的信息。有了这几个阶段,相信很轻松的就可以扩展出任何想要的插件,关键在于插件的行为是由哪些阶段完成的。
自定义标签
这种扩展方式是中间级别的扩展,主要用于当Jsdoc官方提供的标签不能满足文档注释需要的时候,选择自定义标签来补充完善Jsdoc的能力,比如,我需要在文档中注释一个权限声明的标签,@permission,那么可以这样做
1
2
3
4
5
6
7
8
9
10
11exports.defineTags = function(dictionary) {
var opts = {
canHaveName: true,
mustHaveValue: true,
onTagged: function(doclet, tag) {
doclet.permission = doclet.permission || [];
doclet.permission.push(tag);
}
}
dictionary.defineTag("permission", opts);
}Jsdoc提供了
defineTags
接口来自定义标签,其参数dictionary可以理解为标签字典,如上述代码描述,将自定义标签和属性,添加到dictionary中,这样就能被Jsdoc在解析中识别了,onTagged方法定义了当解析到该标签时,Jsdoc应该做出的操作,这里还是doclet,还记得吗?就是那个taffydb的数据,翻到上面那个taffydb数据信息看看,permission就在其中。自定义标签相比上面的EventHandler级别的插件,实现起来简单、方便。自定义抽象语法树访问器
这种扩展方式时最低级别的扩展,官方文档中用了(lowest)一词。同样,来看代码
1
2
3
4
5exports.astNodeVisitor = {
visitNode: function(node, e, parser, currentSourceName) {
// do all sorts of crazy things here
}
};由于直接接触的对象是抽象语法数,那么可做的事情就更多了,就好比越是接近系统底层,可扩展的能力越高,越是上层的东西,可扩展能力就越小,Jsdoc提供了
astNodeVisitor
接口来扩展访问到AST每个节点的行为,感兴趣的同学可以好好实战一番,这里就不过多赘述了
更多
Jsdoc的主要原理和扩展方式在上面分析完了,更多完整功能用法,请参考Jsdoc