最近随着写Node以及独立的CommonJS模块越来越多,我发现有一份好的文档不仅可以帮助自己在应用这些接口的时候不至于迷糊,而且对于共同开发的情况下,能够省去大量团队的交流和Debug的时间。
本文比较了5种较为主流的Javascript注释文档生成工具。需要指出的一点是,Javascript是一个极为灵活的语言,文档生成并不像Java那样具有绝对统一的规范,还要根据使用场景确定哪个工具更加适合。
文中涉及的工具
比较标准
- 生成文档的易读性
- 工具的可扩展性
- 注释语法标准
- 注释语义的丰富程度
长话短说
对没有兴趣了解细节比较的你,可以快速浏览下面的总结来了解这些工具。
YUIDoc是YUI的附属项目。YUI团队希望这个文档工具不仅仅可以支持Javascript,而是更多的脚本语言,因此它并不考虑实际的代码实施细节,而只是对注释部分进行了扫描。从好的一面来说,如果你同时使用了Ruby/PHP/Python等等,YUI或许是一个比较适合的工具。从反面来说,因为它没有考虑实施细节,你需要额外的变量声明、同时生成的文档有可能会和实际的实施细节不吻合。
JSDoc 3的前身是JSDoc Toolkit。它会对代码的具体实施进行扫描,因此你如果不按照符合JSDoc的注释语法来进行注释的话,可能生成的文档一个字也没有。虽然它的学习曲线很高,但是一旦掌握了之后,由于它提供了完整的模版开发,事件触发等等接口,其灵活性也是不容小觑的。
Dox是一个非常轻量级和可以定制化的工具,它使用和JSDoc 3兼容的注释语法标准,并将其转化成JSON格式。因此在呈现方式上具有比JSDoc 3更强的可定制性,当然也意味着你初期的麻烦会比较多。
Docco是一种行间注释的方式。比起API注释,它更适合于注释代码的实施逻辑,以便于后期程序员能够快速捕捉到原作者在此处的实施意图。应该说是非常适合开源项目、多个作者共同维护的一个文档工具。比如最经典的Backbone Annotated Source就是使用Docco进行注释的。
JSDuck由Sencha开发,因此对于自家extJS具有非常强大的支持功能,包括令人咋舌的实时代码修改。但即使你不是使用extJS进行开发,JSDuck仍然是一个非常强大的工具,一方面它的语法非常灵活,描述支持Markdown语法,上手难度远远低于JSDoc 3;另一方面它生成的文档可读性堪比YUIDoc,同时支持源码的对照,学习起来也非常的容易。要说JSDuck有什么不好的地方,估计就是它把一切都Handle太完美了以致于没有给你提供什么可以自己定制的余地。
最后我选择了JSDuck作为文档生成的工具。从目的上说,我需要生成的是提供给Q&A和其他团队成员参考的API文档,考虑到毕竟写代码才是我们的主要工作,注释和文档越简单能够表达意思越好用,而JSDuck恰好十分符合我的需求。但是你选择使用哪种工具还需要根据使用场景来具体考虑,还请参考下面的细节比较
细节比较(JSDoc 3, YUI, JSDuck)
生成文档的易读性(YUIDoc、JSDuck)
YUIDoc
YUIDoc提供了非常清晰的文档格式。不仅对象内部的内容非常清晰,而且相互引用的link也工作的非常好。同时YUIDoc提供了全局搜索的功能,你可以容易根据关键词找到对应的内容。JSDuck
JSDuck生成的文档绝对不输给YUIDoc。构造方法参数、属性、方法都非常清晰的列在文档之中。它的link也非常的好用,能够准确的定位到不同模块中的内容。JSDuck同样提供了全局搜索的方法,而且还在你敲下关键词的同时给你相关的提示。除了这些以外,JSDuck还提供了对于依赖(dependency)、以及查看源码(View source)的方法。JSDoc 3
JSDoc生成的文档使用了Bootstrap样式。默认的样式非常非常的糟糕,不过JSDoc 3在自己的介绍页面里推荐了一个第三方开发的模版系统“Docstrap”。这个模版虽然比JSDoc 3的默认模版好上很多,但是与YUIDoc和JSDuck生成的内容相比就差强人意了。其中的link的锚点也会偶尔不能正常工作。此外,它并不支持全局对于变量的搜索,你可以在Docstrap Github的issue中找到他们相关的开发计划。
除了Docstrap以外,它还有一些推荐的模版系统,例如Jaguar,你也可以在这个基础上开发自己的模版。
工具的可扩展性(JSDoc 3)
YUIDoc
由于YUIDoc是Yahoo下属YUI项目的一个部分,它并不像JSDoc 3提供了那么多可定制的功能。能够修改的大概就只有Logo,基本的CSS样式而已。JSDuck
JSDuck和YUIDoc的情况非常相似,属于Sencha下属的项目。样式上能够修改的也只有Logo而已。不过JSDuck灵活的地方在于,你可以定制文档顶部的Tag:你可以除了文档以外进一步包含视频、教程等等多种内容。-
JSDoc 3
JSDoc 3属于完全开源的项目,因此如果你等不及社区的更新,你完全可以自己对JSDoc进行深度的开发。JSDoc对外提供的开发接口有3个:- 模版Template
- 事件Event
其中最有意思的我认为是事件功能。JSDoc几乎对载入文件、分析注释和生成文档的每一步都提供了事件的hook:parseBegin, fileBegin, beforeParse, jsdocCommentFound……通过这些事件,你甚至可以进一步定义自己需要的comment tag和解析规则。 - 插件Plugins
更加详细的内容则可以在JSDoc的使用说明上找到详细的讲解。
注释语法标准(JSDuck)
最早这一票是投给JSDoc的,但是经过实际的测试之后发现JSDoc的语法标准非常严格,稍微不符合它的解析规则JSDoc便不能够正确的生成文档,其中最让我不能接受的事情是它对于立刻执行函数IIFE的支持实在是%……&*&*……%(里面定义的内容大多被认为是不暴露在全局中的,非常不方便)
下面的说明中我给出了注释一个函数的详细示例。
function exampleName(config, extra){
extra = !!extra; //set the default value of extra to false
this.callback = config.callback;
return this;
}
从这个简单的例子上几乎不太能看出来三者实现的差别,但是当你要注释命名空间、AMD或者CommonJS模块的时候,三者的差别就会凸显出来了。此外要声明的一点是,三者的语法几乎是不能通用的。我曾经试着将用YUIDoc注释的文件使用JSDoc解析,结果非常悲剧,反之亦然。
-
YUIDoc
YUIDoc为了支持多种语言,它仅对注释块内部的内容进行解析。这意味着你需要对于函数、类、命名空间等的名称和更多的内容进行显性的声明。此外,就像前文提到的,它可能会造成文档和代码实现的不一致,甚至对于接口的使用造成不好的影响。
YUIDoc对于注释内容要求严格的两层结构:Primary Tag和Secondary Tag
详细的YUIDoc支持的语法标签可以参考这里/** * My method description. Like other pieces of your comment blocks, * this can span multiple lines. * * @method exampleName 此处必须显性声明method名称 * @param {Object} config A config object * @param {Function} config.callback A callback function on the config object * @param {Boolean} [extra=false] Do extra, optional work * @example * new exampleName(function(){console.log("Hello World")}) * @return {Object} Returns the constructed target object */
-
JSDuck
JSDuck在这点上恰好处在YUIDoc和JSDoc 3之间。它既对代码的实现进行了最基本的解析,从中获取对应的名称、变量,有效的减少了Tag的使用。同时又不像JSDoc 3那样严格,我尝试了CommonJS、AMD和IIFE都能够非常自然的生成对应的文档。
详细的JSDuck支持的语法标签可以参考这里/** * My method description. Like other pieces of your comment blocks, * this can span multiple lines. * * new exampleName(function(){console.log("Hello World")}) JSDuck支持Markdown格式,插入代码示例非常简单 * * @param {Object} config A config object * @param {Function} config.callback A callback function on the config object * @param {Boolean} [extra=false] Do extra, optional work * * @return {Object} The constructed target object * @return {Function} return.callback the callback function of the object JSDuck可以支持返回对象的详细结构注释 */
-
JSDoc 3
JSDoc 3很大程度上参考了Javadoc的实现。它有非常详细的语法规则,并且你只有当和代码实现完全一致的时候,它才能正确的生成文档。但是对于Javascript这样一门灵活的语言而言,这似乎并不是最适合的方式。虽然代码有推荐的最佳实践,但是为了让文档生成工具能够正确识别而要对原本的代码进行修改就有点僭越本份的意味了。/** * My method description. Like other pieces of your comment blocks, * this can span multiple lines. * * @param {Object} config A config object * @param {Function} config.callback A callback function on the config object * @param {Boolean} [extra=false] Do extra, optional work * @example * new exampleName(function(){console.log("Hello World")}) * @returns {Object} The constructed target object */
注释语义的丰富程度(JSDoc 3、JSDuck)
YUIDoc并非针对Javascript量身定制,因此它有一些用法会让你使用的时候感到比较别扭。比如最明显的一个例子就是,YUIDoc并没有一个专门的方法用来注释namespace,而是必须使用@class来对namespace进行注释。
相比之下JSDoc 3和JSDuck都是针对Javascript而设计的,虽然支持的标签略有差别,但是两者都能够很好的支持常见的设计模式。另外,JSDoc 3由于是从JSDoc Toolkit发展而来的,悠久的历史让它在Stackoverflow上有很多不错的例子可以参考。