程序员最讨厌的两件事情,第一种是写文档,另一种是别人没有写文档。有没有直接根据vue组件生成文档的呢?当然是有的的。但第三方使用起来不一定能和现有项目结合使用,往往需要额外的注释用来标记提取信息。使用第三方的一些比较常见问题

  • 文档提取信息不全面,可能有些信息你需要提取但是它又不支持。这种情况下就只能修改三方的插件源码了。
  • 需要额为的注释信息来标记,例如 vuese 需要给方法 打 @vuese@arg 等标记来提供方法信息。

俗话说自己动手丰衣足食,打造自己的vue文档生成工具与自己项目结合使用。一个组件文档大致需要提供 组件名称和描述(name)组件属性(props)组件方法(methods)组件事件(event)插槽(slot) 这几个部分,以及还需要这个几个部分的注释组成生成描述信息。接下来一步步实现对着几个部分的提取实现。

解析.vue 文件

一般一个.vue文件分三个部分 templatescriptstylestyle部分的内容我们不需要,我们需要分别提取出 templatescript 内容。Vue官方开发了 Vue-template-compiler 库专门用于Vue解析,我们可以直接使用它来解析提取.vue文件, Vue-template-compiler 提供了一个 parseComponent 方法可以对原始的Vue文件进行处理。

const compiler = require('vue-template-compiler')
const result = compiler.parseComponent(vueStr, [options]) // parseComponent 返回 template、script、style内容,
export interface SFCDescriptor {
template: SFCBlock | undefined;
script: SFCBlock | undefined;
styles: SFCBlock[];
customBlocks: SFCBlock[];
}

拿到各个部分文本后,还需要将它转成ast(抽象语法树),template 部分内容可以直接使用 Vue-template-compiler 提供的 compile 方法直接生成ast, script部分需要借助其他的生成ast了,这里使用 babel 的模块来处理 js 文本。

const compiler = require('vue-template-compiler')
//vueStr .vue 文件内容
const vue = compiler.parseComponent(vueStr) //生成html部分的 ast
let template = compiler.compile(vue.template.content, {
preserveWhitespace: false,
comments: true // 生成注释信息
})

使用 @babel/parser(Babel解析器,是Babel中使用的JavaScript解析器)来处理js 文本内容。

const parse = require('@babel/parser');

//生成js部分的 ast
let jsAst = parse.parse(vue.script.content, {
allowImportExportEverywhere: true
})

提取文档信息

通过上一步的文件解析工作,我们成功获取到了Vue的模板ast和script中的js的ast,下一步我们就可以从中获取我们想要的信息了。这里需要使用到 @babel/traverse 这个工具,用来遍历 js ast 的节点工具。可以在这里查看 ast 的生成内容,方便查看各种节点信息。

const traverse = require('@babel/traverse');
traverse.default(jsAst, {
enter(path){ // 开始 },
// 支持自定义节点 比如当节点类型 为 ExportDefaultDeclaration 时掉这个方法
ExportDefaultDeclaration(){ }
})

提取组件名称、描述、props、methods、model

export default 生成的对应节点类型是 ExportDefaultDeclaration,declaration 属性就是对应的组件的 options 了,遍历 declaration 的属性可以获取到 namepropsmethodsmodel 等节点信息。

示例

let componentInfo = {}
traverse.default(jsAst, {
ExportDefaultDeclaration(path){
path.node.declaration.properties.forEach(item => {
switch (item.key.name) {
case 'props':
componentInfo.props = extractProps(item) // 提取 props
break;
case 'methods':
componentInfo.methods = extractMethods(item) // 提取 methods
break
case 'name':
componentInfo.name = item.value.value // 获取组件名称
break
case 'model':
componentInfo.model = extractModel(item) // 提取 model
break
default:
break;
}
});
}
})

提取描述

js中注释分为单行和多行两种,生成ast也会生成不同类型的,可以看下面例子。

/**
* 多行备注
* 用来上传文档信息
*/
// 单行备注
export default {
}
// 结尾注释

可以看到会 CommentBlock、 CommentLine 两种类型的节点,还有头部的会放在 leadingComments 里,底部的注释在 trailingComments 里。



一般会把组件描述注释放在 export default 上面,简单提取注释信息

// ExportDefaultDeclaration 插入如下代码
if (path.node.leadingComments) {
componentInfo.desc = path.node.leadingComments.map(item => {
if (item.type === 'CommentLine') {
return item.value.trim()
} else {
return item.value.split('\n').map(item => item.replace(/[\s\*]/g, '')).filter(Boolean)
}
}).toString()
}

提取 methods

因为 methods 中的注释需要额外描述 出参、入参等信息需要额外处理,jsdoc注释规范使用还是比较大众的,这里根据需要自己定义提取规则,还需要提取 async 用来标识是否是异步函数。

/**
* 方法描述
* @param {Bool} type 参数描述
* @returns 返回值描述
*/

提取 props

props 的提取需要区分下面几种情况,default 和 validator 还是提取还是有点麻烦的,validator 校验还可以通过注释简单描述来提取,但是 default 就不好处理了。

{
propA: Number, // 只有类型
propB: [String, Number], // 只有类型但是支持多种
propC: {
type: String,
required: true
},
propD: {
type: Number,
default: 100 // 带有默认值
},
propE: {
type: Object,
default () { // 默认值 需要函数返回
return { message: 'hello' }
}
},
propF: {
default: function () { // 默认值 需要函数返回 和上面的 default 的 ast 节点类型是不同的
return { message: 'hello' }
}
validator: function (value) { // 校验
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}

我这里对 default 处理是借助 @babel/generator 将 default 转换代码, 通过eval转成函数调用返回会默认值。types 是 @babel/types 模块,用来判断节点类型的。

// 获取Props默认值
function getDefaultVal (node) {
if (types.isRegExpLiteral(node) || types.isBooleanLiteral(node) || types.isNumericLiteral(node) || types.isStringLiteral(node)) {
return node.value
} else if (types.isFunctionExpression(node) || types.isArrowFunctionExpression(node) || types.isObjectMethod(node)) {
try {
let code = generate.default(types.isObjectMethod(node) ? node.body : node).code
let fun = eval(**0,${types.isObjectMethod(node) ? 'function ()' : ''} ${code}**)
return JSON.stringify(fun())
} catch (error) {
}
}
}

提取 model

这个比较简单,直接获取就可以了。

提取组件Events

组件的事件没法直接获取到对应节点,只能通过 \(emit()** 方法来定位事件位置,在 **traverse** 中可以使用 **MemberExpress**(复杂类型节点),然后通过节点上的属性名是否是**\)emit 判断是否是事件。

可以看到事件名称在 MemberExpress 父级上的 arguments 里,而备注则在更上一层的里。

const extractEvents = (path) => {
// 第一个元素是事件名称
const eventName = path.parent.arguments[0];
let comments = path.parentPath.parent.leadingComments
return {
name: eventName.value,
desc: comments ? comments.map(item => item.value.trim()).toString() : '——'
}
} MemberExpression (path) {
// 判断是不是event
if (path.node.property.name === '$emit') {
let event = extractEvents(path)
!componentInfo.events && (componentInfo.events = {});
if (componentInfo.events[event.name]) {
componentInfo.events[event.name].desc = event.desc ? event.desc : componentInfo.events[event.name].desc
} else {
componentInfo.events[event.name] = event
}
}
}

在成功获取到Events后,那么结合Events、Props、Model,就可以进一步的判断属性是否支持 .syncv-model

提取组件Slots

首先需要写一个对Vue模板的ast遍历的函数,Vue-template-compiler 没有提供类似于 @babel/traverse 用来 遍历 ast 的。

简单实现个遍历模板抽象树函数

const traverserTemplateAst = (ast, visitor = {}) => {
function traverseArray (array, parent) {
array.forEach(child => {
traverseNode(child, parent);
});
} function traverseNode (node, parent) {
visitor.enter && visitor.enter(node, parent);
visitor[node.tag] && visitor[node.tag](node, parent);
node.children && traverseArray(node.children, node);
visitor.exit && visitor.exit(node, parent);
} traverseNode(ast, null);
}

Vue模板的ast的结构还是比较清晰的,没有js ast 那么多的类型,只需要区分不同tag就可以了。注释会单独一个节点,所以在查找 slot 节点时候,还需要去找它上一个相邻节点,判断是否是注释。

traverserTemplateAst(template.ast, {
slot (node, parent) {
!componentInfo.slots && (componentInfo.slots = {})
// 获取节点位置
let index = parent.children.findIndex(item => item === node)
let desc = '无描述', name = '-';
if (index > 0) {
let tag = parent.children[index - 1]
// isComment 判断是否是 注释
if (tag.isComment) {
desc = tag.text.trim()
}
}
if (node.slotName) name = node.attrsMap.name
componentInfo.slots[name] = {
name,
desc
}
}
})

结语

到这里简单的实现了自动化生成vue组件信息了,当然还有几种情况还没有考虑进去,例如事件$emittemplate 中,slotrender 函数中时候的情,不过提取这部分实现也是大同小异的了。可以在 这里查看 本文源码。

打造自己的Vue组件文档生成工具的更多相关文章

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

    一.现状 Vue框架在前端开发中应用广泛,当一个多人开发的Vue项目经过长期维护之后往往会沉淀出很多的公共组件,这个时候经常会出现一个人 开发了一个组件而其他维护者或新接手的人却不知道这个组件是做什么 ...

  2. 【C#附源码】数据库文档生成工具支持(Excel+Html)

    [2015] 很多时候,我们在生成数据库文档时,使用某些工具,可效果总不理想,不是内容不详细,就是表现效果一般般.很多还是word.html的.看着真是别扭.本人习惯用Excel,所以闲暇时,就简单的 ...

  3. 微软开源全新的文档生成工具DocFX

    微软放弃Sandcastle有些年头了,微软最近开源了全新的文档生成工具DocFX,目前支持C#和VB,类似JSDoc或Sphinx,可以从源代码中提取注释生成文档之外,而且还有语法支持你加入其他的文 ...

  4. .NET平台开源项目速览(4).NET文档生成工具ADB及使用

    很久以前就使用ADB这个工具来生成项目的帮助文档.功能强大,在学习一些开源项目的过程中,官方没有提供CHM帮助文档,所以为了快速的了解项目结构和注释.就生成文档来自己看,非常好用.这也是一个学习方法吧 ...

  5. 【C#附源码】数据库文档生成工具支持(Excel+Htm)

    数据库文档生成工具是用C#开发的基于NPOI组件的小工具.软件源码大小不到10MB.支持生成Excel 和Html 两种文档形式.了解更多,请访问:http://www.oschina.net/cod ...

  6. Markdown 文档生成工具

    之前用了很多Markdown 文档生成工具,发现有几个挺好用的,现在整理出来,方便大家快速学习. loppo: 非常简单的静态站点生成器 idoc:简单的文档生成工具 gitbook:大名鼎鼎的文档协 ...

  7. DBImport v3.44 中文版发布:数据库数据互导及文档生成工具(IT人员必备)

    前言: 距离上一个版本V3.3版本的文章发布,已经是1年10个月前的事了. 其实版本一直在更新,但也没什么大的功能更新,总体比较稳定,所以也不怎么写文介绍了. 至于工作上的事,之前有半年时间跑去学英语 ...

  8. (转)Doxygen文档生成工具

    http://blog.csdn.net/lostaway/article/details/6446786 Doxygen 是一个支持 C/C++,以及其它多种语言的跨平台文档生成工具.如同 Java ...

  9. Sandcastle----强大的C#文档生成工具

    最近客户索要产品的二次开发类库文档,由于开发过程中并没有考虑过此类文档,而且项目规范比较,持续时间比较长,经手人比较多,还真是麻烦,如果人工制作文档需要是一个比较大的工程.还好有这个文档生成工具,能够 ...

随机推荐

  1. 渗透测试工具Burpsuite操作教程

    Burpsuite简介 Burp Suite 是一款专业的Web和移动应用程序渗透测试工具,是用于攻击web 应用程序的集成平台,包含了许多工具.Burp Suite为这些工具设计了许多接口,以加快攻 ...

  2. 怎样用好PS中的钢笔工具(附练习钢笔工具网站)

    想要在PS中得心应手的的描绘出自己想要的线条(也就是路径),就需要对[钢笔工具]有一个充分的理解. [钢笔工具]绘出来的线条全部都是贝赛尔曲线,所以你在学习[钢笔工具]之前,要补习一下贝赛尔曲线的常识 ...

  3. 4、oracle表操作

    4.1.dml操作: 1.查看当前用户下所有的表: select * from user_tables; 2.查看某表的大小: select sum(bytes)/(1024*1024) as &qu ...

  4. Java核心基础第1篇-走进Java世界

    一.Java简介 1.1 Java概述 Java从一开始就以友好的语法.面向对象.内存管理和最棒的跨平台可移植性来吸引程序员. 写一次就可以在所有地方执行( write-once/run-anywhe ...

  5. Docker:docker创建容器时报错:WARNING: IPv4 forwarding is disabled. Networking will not work.

    创建容器时报错: WARNING: IPv4 forwarding is disabled. Networking will not work. # docker run -it -p 30001:2 ...

  6. sshpass用法介绍

    参考文章:http://www.mamicode.com/info-detail-1105345.html https://www.jianshu.com/p/a2aaa02f57dd p.p1 { ...

  7. vsftpd配置文件详解(转)

      vsftpd配置文件详解     1.默认配置: 1>允许匿名用户和本地用户登陆.      anonymous_enable=YES      local_enable=YES 2> ...

  8. 【故障公告】redis 服务器宕机引发博客站点故障

    非常抱歉,今天下午 17:10~17:40 左右,由于博客系统所使用的 redis 服务器宕机,造成博客站点无法正常访问,由此给您带来很大的麻烦,请您谅解. 我们会针对这次故障改进 redis 服务器 ...

  9. postgresql安装及配置

    目录 1. 安装 2. PostgrepSQL的简单配置 2.1 修改监听的ip和端口 2.2 修改数据库log相关的参数 2.3 内存参数 3. 数据库的基础操作 3.1 连接数据库控制台 3.2 ...

  10. 「CF585E」 Present for Vitalik the Philatelist

    「CF585E」 Present for Vitalik the Philatelist 传送门 我们可以考虑枚举 \(S'=S\cup\{x\}\),那么显然有 \(\gcd\{S'\}=1\). ...