当前位置: 首页 > Web前端 > vue.js

解放生产力,自动生成vue组件文档

时间:2023-03-31 16:47:52 vue.js

1、现状Vue框架被广泛应用于前端开发。当一个多人开发的Vue项目长期维护时,往往会沉淀出很多公共组件。这时候,一个A人开发了一个组件,但是其他维护者或者新手不知道这个组件是干什么的,怎么用的,还得再看源码,或者根本没有注意到它的存在完全没有组件,导致重复开发。这时候,维护相应的组件文档,保证不同开发者之间良好的协作关系就显得非常必要了。但是,传统的人工维护文档会带来新的问题:效率低下,编写文档是一项费时费力的工作。组件化开发后很难抽空写文档,想想就头疼。Error-prone,文档内容容易出错,可能与实际组件内容不一致。这不聪明。在组件更新迭代的同时,需要手动将变更同步到文档中,耗时长且容易遗漏。维护文档的理想方式是:工作量小,可以结合Vue组件自动获取相关信息,减少从头开始编写文档的工作量。信息准确,组件关键信息与组件内容一致,无错误。智能同步,Vue组件迭代升级时,可以自动同步更新文档内容,无需人工验证信息是否一致。2.社区解决方案2.1业务组织为了达到以上理想的效果,我在社区中搜索和研究了解决方案。目前,Vue官方提供了Vue-press,可以用来快速构建Vue项目文档,并且已经有可以自动从Vue组件中提取信息的库。但是,现有的第三方库并不能完全满足需求。主要有两个问题:信息不全,一些重要内容无法获取。referencedetails等。比如下面的例子,可以将value属性和input事件结合起来形成一个v-model属性,但是这些信息并没有体现在生成的文档中,需要文档阅读者去理解和判断通过他们自己。并且生成的文档没有显示是否支持同步。自定义logo比较多,logo的命名过于个性化,对原代码的侵入还是比较大的。比如下图的代码中,为了标注注释,需要在原有的业务代码中添加“@vuese”、“@arg”等额外的标签,让业务代码拥有更多的业务——独立的内容。3.技术解决方案针对上述问题以及社区解决方案的缺乏,我们团队沉淀了一个专门用于获取Vue组件信息和输出组件文档的小工具。大体效果如下:上图左边是一个普通的Vue单文件组件,右边是生成的文档。可以看到我们成功的从组件中提取了以下信息:组件的名称。组件的描述。props,slots,events,methods等组件的注解内容。接下来我们将详细解释如何从组件中提取这些信息。3.1Vue文件解析既然要从Vue组件中提取信息,那么第一个问题就是如何解析Vue组件。Vue官方开发了专门用于Vue解析的Vue-template-compiler库,我们这里可以用同样的方法来处理。通过查阅文档,我们可以看到Vue-template-compiler提供了一个parseComponent方法来处理原始的Vue文件。import{parseComponent}from'Vue-template-compiler'constresult=parseComponent(VueFileContent,[options])处理后的结果如下,其中template和script分别对应Vue文件中template和script的文本内容。导出接口SFCDescriptor{模板:SFCBlock|不明确的;脚本:SFCBlock|不明确的;样式:SFCBlock[];customBlocks:SFCBlock[];}当然,仅仅得到文本是不够的,还需要进一步处理才能得到更多的信息。拿到脚本后,我们就可以使用babel将js编译成jsAST(抽象语法树)。这个AST就是一个普通的js对象,可以通过js遍历读取。有了Ast之后,我们也可以得到我们所想的详细的组件信息。从'@babel/parser'导入{parse};常量jsAst=解析(脚本,[选项]);然后我们看模板,继续查找Vue-template-compiler的文档,找到compile方法,compile专门用来将模板编译成AST,刚好符合要求。import{compile}from'Vue-template-compiler'consttemplateAst=compile(template,[options]);result中的ast是模板的编译结果。exportinterfaceCompiledResult{ast:ASTElement,render:string,staticRenderFns:Array,errors:Array}通过第一步的文件解析,我们成功获取了Vue的模板AST的AST和js中的脚本,下一步我们可以从中获取我们想要的信息。3.2信息提取根据是否需要约定,信息可以分为两种:一种是可以直接从Vue组件中获取,比如props、events等,另一种是需要额外约定格式,比如:组件描述注解,props属性描述等。这部分可以放在注解中,通过注解解析得到。为了方便从AST中读取信息,这里简单介绍一个工具@babel/traverse,这是Babel官方提供的用于遍历jsAST的工具。使用方法如下;从'@babel/traverse'traverse(jsAst,options)导入遍历;通过在options中配置相应内容的回调函数,即可获取到想要的ast节点。具体使用请参考官方文档3.2.1可直接获取的信息可直接从代码中获取的信息,可以有效解决信息同步的问题。无论代码如何变化,文档的关键信息都能自动同步,免去人工校对的麻烦。可以直接获取的信息包括:组件属性props提供方法供外部调用methodseventsslotsslots1和2可以直接使用traverse遍历jsAST上名为props和methods的对象节点获取。获取事件稍微麻烦一点。可以通过寻找$emit函数找到事件发生的位置,$emit函数可以在traverse中监听MemberExpress(复杂类型节点),然后判断节点上的属性名是否为'$emit'一个事件。如果它是一个事件,请读取$emitparent中的arguments字段。arguments的第一个元素是事件名称,后面的元素是事件参数。this.$emit('event',arg);traverse(jsAst,{MemberExpression(Node){//判断是否是事件if(Node.node.property.name==='$emit'){//First第一个元素是事件名称consteventName=Node.parent.arguments[0];}}});成功获取Events后,再结合Events和props,可以进一步判断props中的两个特殊属性:v-model是否存在:看props中是否有value属性,Events中是否有输入事件来判断。props的某个属性是否支持sync:判断Events的时间名中是否有update开头的事件,且事件名与属性名相同。slots的信息保存在上面模板的AST中,递归遍历模板AST可以找到名为slots的节点,然后在节点上找到name。3.2.2需要约定的信息为什么除了可以直接获取的组件信息外,是否还有额外约定的部分内容?一是可以直接获取的信息内容比较单薄,不足以支撑一个比较完整的组件文档;另外就是我们每天开发组件的时候写了很多注释。如果我们可以直接提取一些评论放在文档中,那么文档维护的工作量就可以大大减少;顺序可以约定的内容如下:组件名称。组件的整体介绍。道具、事件、方法和插槽的文本描述。方法标签和输入参数的详细描述。这些内容可以在评论中维护。之所以维护在注释中,是因为注释可以很方便的从上面提到的jsAST和templateAST中获取。我们在解析Vue组件信息的时候,可以把这部分有针对性的指令一起解析。接下来重点介绍如何提取注解和注解,以及如何与注解的内容对应起来。js中的注释根据位置的不同可以分为前导注释(leadingComments)和尾随注释(trailingComments)。不同位置的评论将存储在相应的字段中。代码如下://headcommentexportdefault{}//尾部注释分析结果constexportNode={type:"ExportDefaultDeclaration",leadingComments:[{type:'CommentLine',value:'HeadComments'}],trailingComments:[{type:'CommentLine',value:'TrailingComments'}]}在同一位置,根据注释格式的不同分为单行注释(CommentLine)和块级注释(CommentBlock)。两种注释的区别会体现在注释节点的type字段中:/***blockLevelcomment*///单行注释exportdefault{}解析结果constexportNode={type:"ExportDefaultDeclaration",leadingComments:[{type:'CommentBlock',value:'块级注释'},{type:'CommentLine',value:'单行注释'}]}另外,从上面的解析结果我们还可以看出注释节点挂载在注释导出节点中,也解决了我们上面提到的另一个问题:注释与注释关系的获取方式——其实babel在编译代码的时候就已经帮我们搞定了。该模板查找与注释内容不同的注释。模板中的注释节点像其他节点一样作为dom节点存在。遍历节点时,通过判断isComment字段的值是否为true来判断是否为评论节点。注释内容的位置在兄弟节点的最后一位:commentednode解析结果consttemplateAst=[{isComment:true,text:"模板的注释",type:3},{tag:"slot",type:1}]现在我们知道了如何处理评论内容,那么我们就可以利用评论做更多的事情了。比如可以在方法的方法注解中指定一个标签@public来区分是私有方法还是公共方法。更详细的也可以参考另一个专门解析js注解的库js-doc的格式。进一步解释该方法的输入参数,以丰富文档的内容。我们只需要在获取到评论的内容后,对文本进行切割和阅读,例如:exportdefault{methods:{/***@public*@param{boolean}valueentrydescription*/show(value){}}}当然,为了避免过多侵入代码,我们还是需要尽可能少地添加额外的标志。入口描述采用与js-doc相同的格式,主要是因为这种方案应用广泛,代码编辑器自动支持,方便编辑。4.总结写组件文档是一件可以提高项目中前端开发成员之间协作的事情。一个维护良好的文档将极大地改善开发体验。而如果能够进一步利用工具将维护文档的过程自动化,那么开发的幸福感又可以得到提升。经过一系列的探索和尝试,我们成功找到了自动提取Vue组件信息的解决方案,大大减少了维护Vue组件文档的工作量,提高了文档信息的准确性。具体实现上,先用vue-template-compiler对vue文件进行处理,得到template的AST和js的AST。通过这两个AST,您可以获得更详细的信息。让我们整理一下到目前为止生成的内容。文档中可以获取的内容以及获取方法:至于获取到的内容是以Markdown的形式输出还是以json文件的形式输出,要看实际开发情况。5.outlook这里讨论的是直接从单个vue文件中获取并输出信息,但是像elementUI等很多第三方组件库的文档一样,不仅有组件信息,还有显示示例。如果一个组件库维护得比较好,一个组件应该有对应的测试用例,那么是否可以提取组件的测试用例,实现组件文件中example部分的自动提取呢?这也是一个值得研究的问题。作者:vivo互联网前端团队-冯迪