详细介绍如何利用“JSDoc”给“React”项目添加开发文档

程序员最讨厌两件事,第一件是别人的代码里不写注释,第二件是在自己的代码里写注释。

我们的工作中,在遇到项目版本迭代或者是老项目接手时候,刚开始往往非常难上手。究其原因,主要是代码较为凌乱,可读性差。特别是在前端项目中,灵活的JS语法变更较为频繁。如果没有较为详细的代码注释,维护性将变得很差。
作为团队内部的开发规范,可以将代码注释纳入要求。

JSDoc是什么?

JSDoc是一个运行在nodejs中的javascript的api文档生成工具,它能把javascript文件中,特定格式的注释抽离出来,生成较为规范、可读性较高的开发文档。开发人员可以通过阅读JSDoc生成的开发文档,从而查看组件的调用方式、调用参数等等,能极大的提高开发效率,降低代码阅读。

# 全局安装方式:
npm i -g jsdoc 
# 建议安装在项目中的dev依赖下,从而不污染全局命令:
npm i -D jsdoc
# 调用的方式 : npx jsdoc somefileOrPath

JSDoc示例

有文件:needJSDoc.js,内容包含两个实例函数,代码和注释如下:

/**
 * @description 一个简单的求和方法
 * @param {Number} a 第一个加数
 * @param {Number} b 第二个加数
 * @return {Number} result 两个加数的和
 */
export const plus = (a, b) => {
  const result = a + b;
  return result;
};

/**
 * @description 一个简单的求差方法
 * @param {Number} c 被减数
 * @param {Number} d 减数
 * @return {Number} result 差值
 */
export const minus = (c, d) => {
  const result = c - d;
  return result;
};

如果是全局安装的工具,运行全局命令:

 jsdoc needJSDoc.js 

待命令执行完毕后,默认会在同级目录下生成out目录,用浏览器打开该目录中的index.html,有如下展示


可以看出,JSDoc工具识别出代码中的多行注释 /* */。并且根据注释,分析出函数名、参数类型、返回值类型等关键信息。JSDoc还根据注释,生成了可视化的详细规范记录。开发过程中,如需用到plus或者minus方法,可以参考文档传入指定类型的参数,获取指定类型的返回值。

在什么场景下需要JSDoc

建议在如下场景下, 生成并保存前端开发文档以备溯源:

  • 编写的代码为公共类库
  • 项目规模较大,前端项目中超过20个路由地址配置
  • 跨部门、跨团队下进行的结对编程
  • 项目生命周期预计较长,存在版本迭代的可能性较大
  • 短期内项目开发者会有变更

JSDoc的配置

jsdoc支持命令行参数和配置文件两种方式,命令行参数如下:


列举一些可能用到的参数:

-a 不分析注释中 带有特定标识的注释,默认 “-a private” ,则表示不抽离带有 “@private” 标识的注释模块
-c 配置文件相对路径,配置文件应当是json格式
-d 最终生成的文件目录
-e 字符编码,默认utf-8
-P 指定package.json的路径,一般在配置文件中包含了package.json的时候,会用到该参数
-r 递归子目录进行抽离注释工作,如不加该参数,只会处理第一级目录下的文件
-R 指定README.md的路径,一般在配置文件中包含了README.md的时候,会用到该参数
-t 指定使用导出文件的页面模板的路径

# 举个例子: 
# 指定输出目录为 ./docs 而不是默认的 ./out
jsdoc needJSDoc.js -d ./docs 

通过命令行参数来处理开发文档比较繁琐,也不太灵活。临时测试可以使用命令行处理,实际开发环境中,建议通过在项目根目录添加jsdoc.config.json文件来配置JSDoc,配置如下:

{
  "tags": {
    "allowUnknownTags": true,         //是否允许未知的标识
    "dictionaries": ["jsdoc", "closure"]   // 标识规范
  },
  "readme": "README.md",
  "sourceType": "module",
  "source": {
    "include": ["src", "package.json", "README.md"],  //需要包含的文件及目录,这里直接写“src”,包含src下所有文件
    "exclude": [ //排除媒体类、配置工具类等不需要添加注释的文件
      "src/config",
      "src/media",
      "src/util",
      "src/test",
      "src/index.js",
      "src/test.js"
    ],
    "includePattern": ".+\\.(j|t)s(doc|x)?$",       //匹配需要注释的文件后缀
    "excludePattern": "(^|\\/|\\\\)_"               //排除以下划线开头的不需要添加注释的文件
  },
  "plugins": ["plugins/markdown"],   //是否添加插件
  "templates": {       //模板配置
    "cleverLinks": false,
    "monospaceLinks": true,
    "useLongnameInNav": false,
    "showInheritedInNav": true,
    "defailt": {
      "includeData": false
    }
  },
  "opts": {        //参数配置
    "destination": "./dev documentation",   // 输出文档的目录   同 -d
    "template": "node_modules/docdash", // 指定模板    同 -t
    "recurse": true,     //是否递归  同 -r
    "encoding": "utf8"
  },
  "docdash": {  //模板配置,有需要可以添加, 没有需求可以不用设置
    "search": true,
    "typedefs": true,
    "collapse": true,
    "private": false,
    "sectionOrder": [
      "Classes",
      "Modules",
      "Externals",
      "Events",
      "Namespaces",
      "Mixins",
      "Tutorials",
      "Interfaces"
    ]
  },
  "meta": {
    "title": "前端开发文档"
  }
}



结合React项目使用

以create-react-app脚手架为例,在实际开发中可以使用已经开发完成的项目或正在开发中的项目进行配置。

第一步:初始化项目
# 首先创建项目 :
npx create-react-app jsdoc-demo 
# 添加jsdoc工具:
npm i -D jsdoc
第二步: 添加相关配置
# 在package.json同级目录下添加jsdoc.config.json文件
# jsdoc.config.json 内容如下

{
  "tags": {
    "allowUnknownTags": true,
    "dictionaries": ["jsdoc", "closure"]
  },
  "source": {
    "include": ["src", "package.json", "README.md"],
    "includePattern": ".+\\.(j|t)s(doc|x)?$"
  },
  "opts": {
    "destination": "./documentation",
    "recurse": true,
    "encoding": "utf8"
  }
}

# 修改package.json中,script处的命令,添加“doc”脚本,执行“jsdoc”的命令
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
  + "doc":"npx jsdoc -c jsconfig.config.json"
  }
第三步:在组件里添加注释:

示例中单行注释用于说明,块级注释用于JSDoc解析语法

  • 需要导出的函数式组件:
//因为这里的App组件是需要当成模块来 "export default" 导出,所以此处加上 "@module 模块名" 标识
//并且后续export导出时,填写为"@export module:模块名"

/**
 * @description 这是项目开始的组件,
 * 我在这里添加了一行说明,
 * 这是1.0版本的组件
 * @module App
 * @version 1.0
 * @author lean
 * @param {Object} props 入口
 * @return {Fiber} 项目组件
 * @export module:App
 */
export default function App(props) {
  //由于"clickHandle"函数是用"const"关键声明的箭头函数,所以显式的填写"@function 函数名"
  //jsdoc也可以配置babel,进行语法识别,本文不做讨论

  /**
   * @description  标签下的点击事件,在控制台输出Event对象,无返回值
   * @function clickHandle
   * @param {Event} e
   * @return void
   */
  const clickHandle = (e) => {
    console.log(e);
  };

  //jsdoc也可以省略"@description"标识,直接写上说明
  //这里标识了一个常量,用"@memberof" 关键字来标识这个变量是属于某个 类/模块 下的成员
  //这个常量的类型是整型数字,这里 用"@type {int}" 来标识

  /**
   * 这是一个常量整型数字,这个数字控制了xxxxx逻辑
   * @memberof module:App
   * @type {Int}
   */
  const logicNumber = 20;

  return <div className="App"> 
              ...
         </div>;
}
  • 作为内部引用,不需要导出的函数式组件
//不需要导出的函数式组件
//因为jsdoc无法区分当前函数是普通函数还是函数式组件
//所以给函数式组件添加 "@constructor 组件名" 标识,

/**
 * @description 不需要导出的组件
 * @constructor NoExport
 * @param {Object} Props
 * @return {Fiber} 不需要导出的组件
 */
function NoExport(Props) {
  /**
   * @description  标签下的点击事件,在控制台输出Event对象,无返回值
   * @memberof NoExport
   * @function clickHandle
   * @param {Event} e
   * @return void
   */
  const clickHandle = (e) => {
    console.log(e);
  };
  return <div></div>;
}
  • React的类申明式组件
/**
 * @des 这是一个类声明组件
 * @module ComponentWithClass
 * @summary xxxxxxx
 * @extends React.Component
 */
class ComponentWithClass extends React.Component {
  /**
   * @param {Object} props 父级组件下发参数
   */
  constructor(props) {
    super(props);
  }

  /**
   * @description 这是一个异步的函数,它的逻辑是 xxxx....
   * @async
   * @return {String} 返回一些字符串
   */
  async foo() {
    return "xxxxx";
  }

  componentDidMount() {
    this.foo();
  }

  render() {
    return <div></div>;
  }
}

export default ComponentWithClass;
第四步:执行npm run doc命令

执行命令时,会立即生成“documentation”文件夹。
因为配置里包含了package.json和readme.md
所以生成的文件路径会包含项目名和版本号
打开“documentation/ [package.json.name ]/ [package.json.version] /index.html”
而且因为包含了readme.md
会在home目录下显示项目的readme.md说明:


App组件的注释:

类声明组件的注释:异步函数也支持

不需要导出的函数式组件:

结论

JSDoc在React项目中也可以有很好的表现,结合JSDoc,可以很好的维护项目的文档,在多个版本迭代或长时间生命周期下,可以更好的进行组件溯源。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容