程序员最讨厌两件事,第一件是别人的代码里不写注释,第二件是在自己的代码里写注释。
我们的工作中,在遇到项目版本迭代或者是老项目接手时候,刚开始往往非常难上手。究其原因,主要是代码较为凌乱,可读性差。特别是在前端项目中,灵活的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,可以很好的维护项目的文档,在多个版本迭代或长时间生命周期下,可以更好的进行组件溯源。