ES6写JSDoc的一些经验和实例

ES6写JSDoc的一些经验和实例

关于JSDoc和它的基本语法支持,请参考官方文档:http://usejsdoc.org/。虽说官方文档还是比较全的,实际使用中还是需要摸索一下适合自己项目的写法,并考虑最终文档生成工具的支持。

例1:Object类型在function中的定义

假设我们有个需要Object参数的方法。可以这样写:

/**
 * 把两个数字a和b加起来
 * @param {Object} args
 * @prop {number} [args.a] 我是a
 * @prop {number} [args.b=0] 我是b,默认值为0
 * @return {number} 返回a+b
 */
function func({ a, b = 0 }) {
  return a + b;
}

这种写法不允许其他部分重复利用args的结构。

也可以将args独立声明成一个类型:

/**
 * @typedef {Object} FuncArgs
 * @prop {number} [a] 我是a
 * @prop {number} [b=0] 我是b,默认值为0
 */
/**
 * 把两个数字a和b加起来
 * @param {FuncArgs} args
 * @return {number} 返回a+b
 */
function func({ a, b = 0 }) {
  return a + b;
}

这种写法通常用于声明全局或模块通用的类型。

也可以将FuncArgs声明为func的内部类型:

/**
 * @typedef {Object} func~FuncArgs
 * @prop {number} [a] 我是a
 * @prop {number} [b=0] 我是b,默认值为0
 */
/**
 * 把两个数字a和b加起来
 * @param {func~FuncArgs} args
 * @return {number} 返回a+b
 */
function func({ a, b = 0 }) {
  return a + b;
}

这种写法与同样是为func内部类型的第一种写法比起来,更具可读性。但有个个人不太喜欢的地方就是最终生成的文档会将function和参数的类型分成两个部分,对阅读这种文档结构不熟悉的人来说不是很友好。

例2: function作为参数传入的写法

首先,官方设定了一个@callback关键字:

/**
 * @param {func~myCallback} cb
 * @param {number} num
 * @return {number}
 */
function func(cb, num) {
  return cb(num);
}
/**
 * @callback {Function} func~myCallback
 * @param {number} a
 * @return {number}
 */

不过同例1一样,也可以利用@typedef关键字:

/**
 * @param {func~myCallback} cb
 * @param {number} num
 * @return {number}
 */
function func(cb, num) {
  return cb(num);
}
/**
 * @typedef {Function} func~myCallback
 * @param {number} a
 * @return {number}
 */

这两种基本上是一样的,使用@callback更符合语义。另外一点是,这种回调方法一般很少通用,所以建议声明为调用方法的内部类型。

例3: ES6模块default export

/** @module testmodule */
/**
 * @param {number} a I am argument a.
 * @return {string} Return description.
 **/
export default function(a, b) {
  return '' + a + b;
}

jsdoc-to-markdown生成结果:

ES6写JSDoc的一些经验和实例_第1张图片

使用@alias

/** @module testmodule */
/**
 * @alias module:testmodule
 * @param {number} a
 * @return {string} b
 **/
function test(a, b) {
  return '' + a + b;
}

export default test;

jsdoc-to-markdown生成结果跟上面一样。

有趣的是,如果直接export的不是匿名方法,结果会不一样。

/** @module testmodule */
/**
 * @param {number} a I am argument a.
 * @return {string} Return description.
 **/
export default function test(a, b) {
  return '' + a + b;
}

jsdoc-to-markdown生成结果:

ES6写JSDoc的一些经验和实例_第2张图片

例4: 有多个export的ES6模块

jsoc-to-markdown的wiki给了一个非常简单的例子,但只处理了整个文件没有default export的情况: https://github.com/jsdoc2md/jsdoc-to-markdown/wiki/How-to-document-an-ES2015-module-(multiple-named-exports)。

假设有一个ES6文件:

/** @module testmodule */
/**
 * @const
 */
export const testConst = 1;

/**
 * @enum
 */
export const testEnum = {
  'JS': 1,
  'PYTHON': 2,
  'C++': 3
};

/**
 * Function export
 */
export function func() {}

/**
 * @param {number} a I am argument a.
 * @return {string} Return description.
 **/
export default function test(a, b) {
  return '' + a + b;
}

生成结果如下:

ES6写JSDoc的一些经验和实例_第3张图片

这就让人很不解了,为啥要把那些跟default无关的东西放在default下面,混淆视听。

但如果把default export去掉就好了:

ES6写JSDoc的一些经验和实例_第4张图片

但现实是这两种有可能同时出现,我没有找到很好的解决办法,目前只能通过避免出现这种写法或者手动加@memberof module:testmodule,或者更改文档生成模版来修正。

## 例5: 使用Babel

JSDoc还不能聪明到解析ES6代码,为了能让它解析ES6,需要借助于Babel,和插件jsdoc-babel,当然还有相应的Babel preset和插件。

首先安装这些依赖:

npm i --save-dev babel-core babel-preset-env jsdoc jsdoc-babel

然后需要一个JSDoc配置文件, .jsdoc.json(随便什么名字):

{
  "plugins": ["node_modules/jsdoc-babel"]
}

还要一个.babelrc,根据自己的代码来选择preset和插件:

{
  "presets": [
    [
      "env",
      {
        "targets": {
          "browsers": ["last 2 versions", "safari >= 7"]
        },
        "modules": false
      }
    ]
  ]
}

然后运行:

npm run jsdoc -c .jsdoc.json

其他一些状况

有时候模块下的export会被认作内部对象,而不是静态对象

并不是很清楚具体的触发时机,可能是个bug?碰到这种我只能认命加/** @static */来修正结果。

要选对模版

用ES6写JS最大的好处恐怕就是从此多了无数个文件,每个文件里都只是很小一块代码,从此找代码要靠IDE跳转或全文搜索了。So我会希望将一个文件夹作为一个模块,所有此文件夹下的文件通过index.js来选出该模块开放给别人用的东西。
然后我就发现用JSDoc生成文档的时候,要做到这点比较难。
第一是JSDoc似乎并不能识别文件路径,如果要将多个文件声明属于同一个模块,只有在每个文件头部手动加上/** @module ModuleName */
但这一切,还需要模版的帮忙。我试用了nijikokun/minami和tui.jsdoc-template。tui.jsdoc-template很好的实现了我想要的这种树状的结构效果:
tui jsdoc template

还有要考虑的一点是/** @enum */这个关键字,个人认为是应该要在生成文档时列举出声明的键和值的。但同样的,要看模版支不支持。

支持flow

flow的支持也是简单地靠Babel插件transform-flow-strip-types来完成。
特地拿出来讲是因为我曾很美好地设想可以直接在flow type的声明下面,加上结构特别类似的JSDoc和必要的描述说明文字。把flow type声明转成JSDoc这一步甚至可以完全通过脚本自动完成,会省不少力气。
结果发现如果一个文件纯粹只有flow声明和注释,transform-flow-strip-types这个插件会直接将整个文件的内容干掉,导致里面的JSDoc丢失。
然后flow type声明转成JSDoc这个我刚开始就放弃了,要做到这点我需要一点点分析AST结构,将每种情况对应成JSDoc,很费力而且价值不是很大。

你可能感兴趣的:(工具,学习笔记)