如何开发一个vscode插件自动生成React组件文档

如何开发一个vscode插件自动生成React组件文档

test.gif

背景

组件化开发早已成为前端开发者必备技能之一了,在团队协作中,一个组件开发完成后,往往是需要被其他同事使用的,随之而来的问题就是“你这个组件怎么用呀?”,因此配套的组件文档是必不可少的,但同时也会带来其他问题:

  • 码字耗时,增加了开发时间
  • 文档格式不统一,不规范

因此,为了解决以上问题,自动生成统文档已经急不可待。考虑到团队中大部分小伙伴使用的IDE是vscode,且不想对旧代码、旧文档有浸入式修改,故通过vscode extension的形式做文档优化。

阅读本文,你将了解到:

  • 如何使用react-docgen开发一个vscode插件,自动生成react jsx组件文档,效果如上图
  • react-docgen(其他前端工具、插件)赖以生存的抽象语法树AST

准备

前置知识和依赖、环境安装。

react-docgen

react-docgen是一个CLI和工具箱,帮助从React组件中提取信息,并从中生成文档。它使用ast-types和@babel/parser将源解析成AST,并提供方法来处理这个AST以提取所需的信息。输出/返回值是一个JSON blob/JavaScript对象。

要使用一个组件,我们需要知道组件基本信息,如用途、传参,最好还能有举例。props是react组件间通讯最常用方式之一,组件内可通过prop-typesdefaultProps来做类型检查和设置默认值,而react-docgen可通过它们和jsdoc注释内容来获取组件信息。具体安装和使用规则,可点这里查看。

vscode extension

关于vscode插件的开发环境搭建教程网上有很多,这里就不进行赘述了,具体可以参考官方文档或者这里。执行脚手架命令后,生成目录大致如下:

vscode-react-doc                // 插件根目录
  ├── src                       // 源码
  │   ├── commands              // 命令文件夹
  │   │   ├── jsx2md.js    
  │   │   ├── ...
  │   ├── utils                 // 放自定义生成文档方法
  │   ├── extension.js          // 插件入口文件
  ├── README.md                 // 插件说明            
  └── package.json              // 项目配置文件
  
复制代码

开发

安装好react-docgenvscode extension脚手架后环境后,即可开始开发,这里以一个FC组件为例(class同样支持)。

组件Button

使用prop-typesdefaultProps设置默认props,使用/** ... */注释。

import React from "react";
import PropTypes from "prop-types";

/**
 * 自定义信息,用在生成组件使用
 * @description 我是组件简介
 * @author 我是组件开发者
 * @remark 我是备注
 */
function Button(props) {
  const { text, clickHandle } = props;
  return <div onClick={clickHandle}>{text}</div>;
}

Button.propTypes = {
  /**
   * 按钮文字
   */
  text: PropTypes.string,
  /**
   * 按钮点击事件
   */
  clickHandle: PropTypes.func.isRequired,
};

Button.defaultProps = {
  text: "按钮文字",
};

export default Button;
复制代码

配置命令

分别配置extension.jspackage.json,这里新增一个名为jsx2md的命令,在选中jsx组件文件时激活插件。

// extension.js 
const vscode = require("vscode");
const jsx2md = require("./commands/jsx2md.js");

//销毁插件回调函数
function activate(context) {
  console.log("插件激活成功");

  //注册命令
  const jsx2mdCmd = vscode.commands.registerCommand(
    "vscode-react-doc.jsx2md",  //命令名:jsx2md
    (fileUri) => {
      jsx2md(fileUri);  //将选择的文件作为参数传入
    }
  );

  //订阅命令,可push多个
  context.subscriptions.push(jsx2mdCmd);
}
//销毁插件回调函数
function deactivate() {}

module.exports = {
  activate,
  deactivate,
};

复制代码
// package.json
{
    ...
    // 什么情况下可激活插件
    "activationEvents": [
        //onCommand表示输入命令时激活,和extension.js统一
        "onCommand:vscode-extension-demo.jsd2md" 
    ],
    // 入口文件
    "main": "./src/extension.js",
    // 插件配置项
    "contributes": {
        //菜单栏
        "menus": {
            //IDE右键菜单栏
            "editor/context": [
                {
                    "when": "resourceExtname == .jsx", //选择文件后缀名
                    "command": "vscode-react-doc.jsx2md",
                    "group": "navigation"
                }
            ],
            //文件夹右键菜单栏
            "explorer/context": [
                {
                    "when": "resourceExtname == .jsx",
                    "command": "vscode-react-doc.jsx2md",
                    "group": "navigation"
                }
            ]
        },
        //注册的命令,和extension.js统一
        "commands": [
           {
               "when": "editorLangId == javascriptreact",
               "command": "vscode-react-doc.jsx2md",
               "title": "jsx2md"
           }
        ]
    }
  ...
}
复制代码

解析组件,生成文档

使用react-docgen解析组件文件,生成AST并返回组件信息json对象,使用正则/操作字符串方式操作json,根据自身需求,生成文档内容,并复制粘贴使用。

// jsx2md.js
const fs = require("fs-extra");
const prettier = require("prettier");
const vscode = require("vscode");
const reactDocs = require("react-docgen");

const jsx2md = (fileUri) => {
    const uri = fileUri.fsPath;
    // 生成的文档内容
    let resultMd = "";
    // 文件原始内
    const originContent = fs.readFileSync(uri, "utf-8");
    // 解析后的组件信息,json格式
    const componentInfo = reactDocs.parse(originContent);
    
    // 根据componentInfo内容,自定义生成文档内容(字符串相关操作)
    // ...

    // 使用prettier格式化文档内容
    const formatMd = prettier.format(resultMd, {
        parser: "markdown",
    });
    
    //复制文档内容
    try {
        vscode.env.clipboard.writeText(resultMd);
        vscode.window.showInformationMessage(
          `${resultMd ? "文档生成成功,可粘贴使用" : "无可用信息"}`
        );
    } catch (error) {
        vscode.window.showErrorMessage(error.message);
    }
}

module.exports = jsx2md;
复制代码

componentInfo打印出来如下:

{
    description:'@description 我是组件简介\n@author 我是组件开发者\n@remark 我是备注',
    displayName:'Button',
    methods:[],
    props:{
        text: {
            defaultValue:{
                computed:false,
                value:'"按钮文字"'
            },
            description:'按钮文字',
            required:false,
            type:{
                name:'string'
            }
        }, 
        clickHandler: {
            description:'按钮点击事件'
            required:true,
            type:{
                name:'func'
            }
        }
    }

复制代码

主要包含:

  • displayName 组件名称
  • description 组件的自定义信息注释
  • methods 组件内部的方法
  • props 组件的属性参数

其中这里的props是我们组件文档的核心内容,在提取的内容中,已经涵盖了属性的 属性名属性描述类型默认值是否必传,加上自定义生成一个用例说明,这些内容基本可满足我们阅读组件文档所需要的信息。

如果有相同应用场景的小伙伴,可以在插件市场搜索vscode-react-doc下载使用,或者点击这里下载。

拓展

react-docgen原理是通过babel解析文件内容,生成AST,再转成一个含组件信息的json。其实,在前端领域中,AST影子随处可见,在这里,我们继续深入探究一下它吧。

什么是AST

在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

这里借用工具ast explorer来进一步了解AST,在工具中左边输入:

let sum = 1 + 2;
复制代码

在工具右边,会生成当前代码对应的AST

{
    "type": "Program",
    "start": 0,
    "end": 17,
    "body": [
        {
            "type": "VariableDeclaration",
            "start": 0,
            "end": 16,
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "start": 4,
                    "end": 15,
                    "id": {
                        "type": "Identifier",
                        "start": 4,
                        "end": 7,
                        "name": "sum"
                    },
                    "init": {
                        "type": "BinaryExpression",
                        "start": 10,
                        "end": 15,
                        "left": {
                            "type": "Literal",
                            "start": 10,
                            "end": 11,
                            "value": 1,
                            "raw": "1"
                        },
                        "operator": "+",
                        "right": {
                            "type": "Literal",
                            "start": 14,
                            "end": 15,
                            "value": 2,
                            "raw": "2"
                        }
                    }
                }
            ],
            "kind": "let"
        }
    ],
    "sourceType": "module"
}
复制代码

AST如何生成

AST是通过js parser(解析器),将js源码转化成抽象语法树,主要分为两步:

词法分析

读取代码中的字符流,然后通过词法分析生成每个不可分割的最小语法单元token,js中的保留关键字、运算符、括号、数字、字符串都能作为token,如上面的例子,let就为一个token。最后,整个代码将被分割进一个数组tokens中:

[
  { type: "Keyword", value: "let", range: [0, 3] },
  { type: "Identifier", value: "sum", range: [4, 7] },
  { type: "Punctuator", value: "=", range: [8, 9] },
  { type: "Numeric", value: "1", range: [10, 11] },
  { type: "Punctuator", value: "+", range: [12, 13] },
  { type: "Numeric", value: "2", range: [14, 15] },
  { type: "Punctuator", value: ";", range: [16, 17] },
]
复制代码

语法分析

遍历tokens,识别语句(statement)和表达式(expression),转化成树形结构。同时,检查语法准确性,如果有错误,则抛出语法错误,没有错误则输出上述的AST。

AST在前端的应用

image.png
如上图,在前端工程化过程中,那些我们日夜相对的插件、工具,无不都是基于AST上开发:

  • ES6+转ES5(babel)
  • 模块打包(webpack)
  • 代码校验(eslint)
  • 代码美化(prettier)
  • css预处理(less/sass)
  • vue中template、react中jsx的编译
  • taro等跨平台框架

由此可见AST不可或缺,学习它不仅能加深我们平时对上面列举的各种神器原理的学习,在需要的时候,我们也能利用它来解决自己需求场景内的问题。

参考

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享