解放生产力,自动化生成vue组件文档
一、现状
Vue框架在前端开发中应用广泛,当一个多人开发的Vue项目经过长期维护之后往往会沉淀出很多的公共组件,这个时候经常会出现一个人 开发了一个组件而其他维护者或新接手的人却不知道这个组件是做什么的、该怎么用,还必须得再去翻看源码,或者压根就没注意到这个组件 的存在导致重复开发。这个时候就非常需要维护对应的组件文档来保障不同开发者之间良好的协作关系了。
但是传统的手动维护文档又会带来新问题:
效率低,写文档是个费时费力的体力活,好不容易抽时间把组件开发完了回头还要写文档,想想都头大。
易出错,文档内容容易出现差错,可能与实际组件内容不一致。
不智能,组件更新迭代的同时,需要手动将变更同步到文档中,消耗时间还容易遗漏。
而理想中的文档维护方式则是:
工作量小,能够结合Vue组件自动获取相关信息,减少从头开始写文档的工作量。
信息准确,组件的关键信息与组件内容一致,不出错。
智能同步,Vue组件迭代升级时,文档内容可以自动的同步更新,无需人工校验信息是否一致。
二、社区解决方案
2.1 业务梳理
为了能实现上述理想效果,我搜索并研究了一下社区中的解决方案,目前Vue官方提供了Vue-press可以用于快速搭建Vue项目文档, 而且也已经有了可以自动从Vue组件中提取信息的库了。
但是已有的第三方库并不能完全满足需求,主要存在以下两个问题:
信息不全面,一些重要内容无法获取例如不能处理v-model,不能解析属性的修饰符sync,不能获取methods中函数入参的详细信息等。
比如下面的例子,value属性与input事件可以合起来构成一个v-model属性,但是这个信息在生成的文档中没有体现出来,要文档读者自行理解判断。而且生成的文档中没有展示是否支持sync。
有较多的自定义标识,而且标识的命名过于个性化,对原有的代码侵入还是比较大的。例如下图中的代码,为了标记注释,需要在原有的 业务代码中额外添加"@vuese" "@arg"等标识,使得业务代码多出了一些业务无关内容。
三、技术方案
针对以上文中提到的问题以及社区方案的不足,我们团队内沉淀出了一个小工具专门用于Vue组件信息获取并输出组件文档,大致效果如下:
上图中左边是一个常见的Vue单文件组件,右边是生成的文档。我们可以看到我们从组件中成功的提取到了以下一些信息:
组件的名称。
组件的说明。
props,slot,event,methods等。
组件的注释内容。
接下来我们将详细的讲解如何从组件中提取这些信息。
3.1 Vue文件解析
既然是要从Vue组件中提取信息,那么首先的问题就是如何解析Vue组件。Vue官方开发了Vue-template-compiler库专门用于Vue解析, 这里我们也可以用同样的方式来处理。通过查阅文档可知Vue-template-compiler提供了一个parseComponent方法可以对原始的Vue文件进行处理。
import { parseComponent } from 'Vue-template-compiler'
const result = parseComponent(VueFileContent, [options])
处理后的结果如下,其中template和script分别对应Vue文件中的template和script的文本内容。
export interface SFCDescriptor {
template: SFCBlock | undefined;
script: SFCBlock | undefined;
styles: SFCBlock[];
customBlocks: SFCBlock[];
}
当然仅仅是得到文本是不够的,还需要对文本进行更进一步的处理来获取更多的信息。得到script后,我们可以用babel把js编译成js的AST(抽象语法树),这个AST是一个普通的js对象,可以通过js进行遍历和读取 有了Ast之后我们就可以从中获取到我们想到详细的组件信息了。
import { parse } from '@babel/parser';
const jsAst = parse(script, [options]);
接着我们来看template,继续查找Vue-template-compiler的文档我们找到compile方法,compile是专门用于将template编译成AST的, 正好可以满足需求。
import { compile } from 'Vue-template-compiler'
const templateAst = compile(template, [options]);
得到结果中的ast则为template的编译结果。
export interface CompiledResult {
ast: ASTElement,
render: string,
staticRenderFns: Array<string>,
errors: Array<string>
}
通过第一步的文件解析工作,我们成功获取到了Vue的模板ast和script中的js的AST,下一步我们就可以从中获取我们想要的信息了。
3.2 信息提取
根据是否需要约定,信息可以分为两种:
一种是可以直接从Vue组件中获取,例如props、events等。
另一种是需要额外约定格式的,例如:组件的说明注释,props的属性说明等,这部分可以放到注释里,通过对注释进行解析获取。
为了方便的从ast中读取信息,这里先简单介绍一个工具@babel/traverse,这个库是babel官方提供的专门用于遍历js AST的。使用方式如下;
import traverse from '@babel/traverse'
traverse(jsAst, options);
通过在options中配置对应内容的回调函数,可以获得想要的ast节点。具体的使用可以参考官方文档
3.2.1 可直接获取的信息
可以从代码中直接获取的信息可以有效的解决信息同步问题,无论代码怎么变动,文档的关键信息都可以自动同步,省去了人工校对的麻烦。
可以直接获取的信息有:
组件属性props
提供外部调用的方法methods
事件events
插槽slots
1、2都可以利用traverse在js AST上直接遍历名称为props和methods的对象节点获取。
事件的获取稍微麻烦一点,可以通过查找$emit函数来定位到事件的位置,而$emit函数可以在traverse中监听MemberExpress(复杂类型节点), 然后通过节点上的属性名是否是'$emit'判断是否是事件。如果是事件,那么在$emit父级中读取arguments字段, arguments的第一个元素就是事件名称,后面的元素为事件传参。
this.$emit('event', arg);
traverse(jsAst, {
MemberExpression(Node) {
// 判断是不是event
if (Node.node.property.name === '$emit') {
// 第一个元素是事件名称
const eventName = Node.parent.arguments[0];
}
}
});
在成功获取到Events后,那么结合Events和props,就可以进一步的判断出props中的两个特殊属性:
是否存在v-model:查找props中是否存在value属性并且Events中是否存在input事件来确定。
props的某个属性是否支持sync:判断Events的时间名中是否存在有update开头的事件,并且事件名称与属性名相同。
插槽slots的信息保存在上文的template的AST中,递归遍历template AST找到名为slots的节点,进而还可以在节点上查找到name。
3.2.2 需要约定的信息
为什么除了可直接获取的组件信息之外,还会需要额外的约定一部分内容呢?其一是因为可直接获取的信息内容比较单薄,还不足以支撑起一个相对完善的组件文档;其二是我们日常开发组件时本身就会写很多的注释,如果能直接将部分注释提取出来放到文档中,可以大大降低文档维护的工作量;
整理一下可以约定的内容有以下几条:
组件名称。
组件的整体介绍。
props、Events、methods、slots文字说明。
Methods标记和入参的详细说明。这些内容都可以放在注释中进行维护,之所以放在注释中进行维护是因为注释可以很容易从上文提到的js AST以及template AST中获取到, 在我们解析Vue组件信息的同时就可以把这部分针对性的说明一起解析到。
接下来我们着重讲解如何将提取注释和注释与被注释的内容是如何对应起来的。
js中的注释根据位置不同可以分为头部注释(leadingComments)和尾部注释(trailingComments),不同位置的注释会存放在对应的字段中, 代码展示如下:
// 头部注释export default {} // 尾部注释
解析结果
const exportNode = {
type: "ExportDefaultDeclaration",
leadingComments: [{
type: 'CommentLine',
value: '头部注释'
}],
trailingComments: [{
type: 'CommentLine',
value: '尾部注释'
}]
}
在同一个位置上,根据注释格式的不同又分为单行注释(CommentLine)和块级注释(CommentBlock),两种注释的区别会反应在注释节点的type字段中:
/**
* 块级注释
*/
// 单行注释
export default {}
解析结果
const exportNode = {
type: "ExportDefaultDeclaration",
leadingComments: [
{
type: 'CommentBlock',
value: '块级注释'
},
{
type: 'CommentLine',
value: '单行注释'
}
]
}
另外,从上面的解析结果我们也可以看到,注释节点是挂载在被注释的export节点里面的,这也解决我们上面提到的另一个问题:注释与被注释的关联关系怎么获取的--其实babel在编译代码的时候已经替我们做好了。
template查找注释与被注释内容的方法不同。template中注释节点与其他节点一样是作为dom节点存在的, 在遍历节点的时候通过判断isComment字段的值是否为true来确定是否是注释节点。而被注释的内容的位置在兄弟节点的后一位:
<!--template的注释-->
<slot>被注释的节点</slot>
解析结果
const templateAst = [
{
isComment: true,
text: "template的注释",
type: 3
},
{
tag: "slot",
type: 1
}
]
知道了如何处理注释内容,那么我们还可以利用注释做更多的事情。例如可以通过在methods的方法的注释中约定一个标记@public来区分是私有方法还是公共方法,如果更细节一点的话, 还可以参考另一个专门用于解析js注释的库js-doc的格式,对方法的入参进行更进一步的说明,丰富文档的内容。
我们只需要在获取到注释内容之后对文本进行切割读取即可,例如:
export default {
methods: {
/**
* @public
* @param {boolean} value 入参说明
*/
show(value) {}
}
}
当然了为了避免对代码侵入过多,我们还是需要尽量少的添加额外的标识。而入参说明采用了与js-doc相同的格式,主要还是因为这套方案 使用比较普遍,而且代码编辑器都自动支持方便编辑。
四、总结
编写组件文档是一个可以很好的提升项目内各个前端开发成员之间协作的事情,一份维护良好的文档会极大的改善开发体验。而如果能进一步的使用工具把维护文档的过程自动化的话,那开发的幸福感还能得到再次提升。
经过一系列的摸索和尝试,我们成功的找到了 自动化提取Vue组件信息的方案,大大减轻了维护Vue组件文档的工作量,提升了文档信息的准确度。具体实现上,先用vue-template-compiler对Vue文件进行处理,获得template的AST和js的AST,有了这两个AST后就可以去获取更加详细的信息了, 梳理一下到目前为止我们生成的文档里可以获取到的内容及获取方式:
至于获取到内容之后是以Markdown的形式输出还是json文件的形式输出,就取决于实际的开发情况了。
五、展望
这里我们所讨论的是直接从单个Vue文件去获取信息并输出,但是像很多第三方组件库里例如elementUI的文档,不仅有组件信息还有展示实例。如果一个组件库维护的相对完善的话,一个组件应该会有对应的测试用例,那么是否可以将组件的测试用例也提取出来, 实现组件文件中示例部分的自动提取呢?这也是值得研究的问题。
作者:vivo互联网前端团队-Feng Di
解放生产力,自动化生成vue组件文档的更多相关文章
- vite插件-自动生成vue组件文档
特点 支持热更新 快速启动,依赖于 vite,无需另起服务 自动生成组件导航 ui 采用了vant-ui的样式 核心方法覆盖率达到了 92.86% 使用 yarn add vite-plugin-vu ...
- 打造自己的Vue组件文档生成工具
程序员最讨厌的两件事情,第一种是写文档,另一种是别人没有写文档.有没有直接根据vue组件生成文档的呢?当然是有的的.但第三方使用起来不一定能和现有项目结合使用,往往需要额外的注释用来标记提取信息.使用 ...
- 解析Markdown文件生成React组件文档
前言 最近做的项目使用了微前端框架single-spa. 对于这类微前端框架而言,通常有个utility应用,也就是公共应用,里面是各个子应用之间可以共用的一些公共组件或者方法. 对于一个团队而言,项 ...
- Vue 学习文档
Vue 学习文档 vue 起步 引包 启动 new Vue(options) options: el 目的地(可以用类名.标签名等,也可以直接用mod元素) #elementId .elementCl ...
- Docz 用 MDX 写 React UI 组件文档
Docz 用 MDX 写 React UI 组件文档 前言 为了提升开发效率,创建一套 UI 组件库是一种较为有效的方式之一:可以减少重复工作.提高可复用,所以现在越来越多团队开始创建自己的 UI 组 ...
- 如何生成一个java文档
如何生成一个java文档 众所周知,一个程序给别人看可能可以看懂,几万行程序就不一定了.在更多的时候,我们并不需要让别人知道我们的程序是怎么写的,只需要告诉他们怎么用的.那么,api文档就发挥了它的作 ...
- 使用 Sandcastle 生成代码帮助文档
使用 Sandcastle可以生成MSDN风格的帮助文档,生成的帮助文档既可以是chm文档,也可以是MS Help 2.x帮助文档. 1 下载并安装Sandcastle Sandcastle下载地址为 ...
- Swagger+Spring mvc生成Restful接口文档
简介 Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.总体目标是使客户端和文件系统作为服务器以同样的速度来更新.文件的方法,参数和模型紧密集 ...
- GhostDoc:生成.NET API文档的工具 (帮忙文档)
在 Sandcastle:生成.NET API文档的工具 (帮忙文档) 后提供另一个生成API文档的工具. 1) 准备工作 安装GhostDoc Proc. 收费的哦.... 这个工具的优势是不像 ...
随机推荐
- 【BUAA软工】Beta阶段事后分析
设想与目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 解决的问题 总体解决的问题:新手编程者配置编程环境难.本地编写的代码跨设备同步难.本地ide安装使用过程 ...
- 网速测试利器-iperf3
网速测试利器-iperf3 使用工具 简介 iperf3是一个网络速度测试工具,支持IPv4与IPv6,支持TCP.UDP.SCTP传输协议,可在Windows.Mac OS X.Linux.Fr ...
- openstack创建vlan网络并配置网络设备
1.在管理员-->网络-->创建网络. 2.填写网络信息,这里要划分新的VLAN,注意在物理网络中填写的事VLAN,段ID指的是vlan的id 3.创建的网络. 4.创建子网,在里面修改子 ...
- Lua中的异常处理pcall、xpcall、debug使用实例
如果需要在Lua中处理错误,必须使用函数pcall(protected call)来包装需要执行的代码. pcall接收一个函数和要传递个后者的参数,并执行,执行结果:有错误.无错误:返回值true或 ...
- Kafka 的安装及启动
Linux 服务器 CentOS 为例. Kafka 安装 获取下载地址,下载后解压. $ wget https://mirror.bit.edu.cn/apache/kafka/2.5.0/kafk ...
- Spring-Cloud之Feign原理剖析
Feign 主要是帮助我们方便进行rest api服务间的调用,其大体实现思路就我们通过标记注解在一个接口类上(注解上将包含要调用的接口信息),之后在调用时根据注解信息组装好请求信息,接下来基于rib ...
- Locust性能测试工具核心技术@task和@events
Tasks和Events是Locust性能测试工具的核心技术,有了它们,Locust才能称得上是一个性能工具. Tasks 从上篇文章知道,locustfile里面必须要有一个类,继承User类,当性 ...
- Python - random 库的详细使用
前言 为啥突然写这个?因为用到就写呗,感觉对生成数据很有用,之前都是百度别人的,今天来对着官方文档写,超级标准! 这边只讲常用的,看了下文档还有什么数学方法,太高级好像用不上 返回整数 random. ...
- node.js学习(7)流和管道
1 导入模块 输入流 # 读取流 # 写入流 # # 管道 # 压缩 # 解压缩
- FPGA多功能应用处理器
FPGA多功能应用处理器 编解码加速卡 概述: 对于H.265/HEVC/VP9编解码处理,FPGA编解码加速卡方案有着完善的功能和preset配置,支持最多的有利于提高画质和降低bitrate的功能 ...