你的.vue文件就已经是你的文档了
昨天发布了vuese1.0,这是我的一个新的开源项目,用来解析Vue SFC并生成markdown文档,这里: github.com/HcySunYang/… 。
这篇文章不会介绍如何使用,至于如何使用大家可以查看 readme,这里我们主要说一说实现的思路。
一、动机
你或者你的团队也许会有一套自己的组件库(或者是单纯的一个组件),通常当你开发完一个组件之后,你需要手动的编写markdown文档,从而让其他人了解组件是如何使用的。这里的问题在于:假若组件不停的在更新,你就必须要不停的手动维护对应的文档,使其与组件的功能保持一致。实际上这个过程是有些恶心🤢的,那么有什么更好的办法吗?基于此我才开发了 vuese 。
二、想法
我们知道一个 vue
组件所暴露的接口无非是 props
、events
、slots
(或scopedSlots
) 以及部分 methods
。那我们可不可以实现一个工具帮助我们分析一个vue组件并提取这些信息呢?然后自动生成文档,这样无论组件如何改动我们都不需要手动维护文档了,只需要使用该工具重新生成即可。
三、基本思路
对于一个 vue
组件,如果我们抛开 style
和自定义块,那么它由两部分组成,即:模板 和 script
块。甚至如果使用 render
函数代替模板的话,那么就只剩一下一个 script
了。
对于 props
,methods
它们只能在 scirpt
块内定义,如:
props: {
name: String
}
methods: {
clear () {/*...*/}
}
而对于 slots 则既可以在 script 块内定义,也可以在模板中定义,如:
<!-- 在模板中定义 -->
<div>
<slot name="header" />
</div>
// 在 script 块内定义了 slots
render (h) {
return h('div', this.$slots.header)
}
以上两种写法是等价的,所以在提取 slots
信息时即需要考虑模板中的slots
,也要兼容 script
块内的 slots
。
而且在函数式组件中,以下内容都应该作为 slots 处理:
// ctx.slots()
render (h, ctx) {
return h('div', ctx.slots().xxx)
}
// ctx.children
render (h, ctx) {
return h('div', ctx.children)
}
这也是我们在提取slots
信息时需要考虑在内的。
同样的,对于 events
而言也是既可以出现在模板中,又可以出现在 script
块中,如下:
<!-- 模板中的 events -->
<div @click="$emit('onclear')"></div>
// 定义在 script 中的事件
methods: {
someMethod () {
this.$meit('onclear')
}
}
所以在提取 events
信息时也需要即考虑模板又考虑 script
块。
对于模板我们默认是 html
语法,对于 script
块我们默认为 js
。我们要做的第一件事儿就是将 html
和 js
单独提取出来并单独分析,好在巨人的肩膀厚实,已经有了 @vue/component-compiler-utils
模块和 vue-template-compiler
模块。其中我们使用 @vue/component-compiler-utils
模块解析 vue SFC 并分别得到 html(模板) 和 JavaScript(script块) 的源码。对于 html 源码我们可以再次使用 vue-template-compiler
模块将其解析为模板对应的 AST
,然后通过编写一个 traverse
函数对其进行分析,读取slots
相关的内容。
而对于 JavaScript
源码的处理,我选择了使用 babel7
,写过 babel
插件的同学或许已经猜到了实现的思路。我们将源码交由 @babel/travers
模块处理,然后通过编写一些 helper
函数来辅助我们判断出哪些是要真正处理的内容即可,如下源码段所示:
const mainTraveres = {
ObjectProperty(path: any) {
// Processing name
if (isVueOption(path, 'name')) {
if (onName) onName(path.node.value.value)
}
// Processing props
if (isVueOption(path, 'props')) {
// 一些逻辑
}
// more...
}
}
其中 isVueOption
函数是我们自己编写的 helper
函数,来辅助我们判断一个对象的属性是否是 Vue
选项对象中的特定属性,如果是我们就进一步处理就可以了,对于 js 的处理基本都是这个思路,更多内容大家可以查看源码:github.com/HcySunYang/…
四、生成目标
经过上一步的处理,我们可以编写出一个 parser
模块,解析并组装出我们需要的内容,有了需要的内容之后,我们就可以根据这些信息编写一个 Render
模块,本质就是一个代码生成的过程,至于生成的内容是什么这取决于你想要的目标,vuese 内置的 Render
会根据这些信息为你生成 markdown
文件,或者生成一个集成 docute
的文档。但实际上只要你脑洞够大你可以生成任何东西,试想一下,如果我们的 parser
模块编写的更加完善,对一个 vue
组件的分析足够细致,这样我们就能拿到一个 vue
组件全部的信息,然后在代码生成阶段将其生成一个 ts compatible 的组件,这不就实现了一个将非js编写的vue组件转换为ts兼容的vue组件的插件了吗?(备注:后来一哥们儿提供了一个更好的办法来实现这件事儿,为了避免尴尬,我只能说这也是一个思路嘛......)
回到 vuese 内置的 Render 模块,Render 的结果就是markdown资源,本质就是一个字符串拼接的过程,具体可以查看源码 github.com/HcySunYang/… ,并不复杂。
实际上 vuese 提供了很多有用的信息和文档中没有体现出来的功能,这多会在后续逐渐补充。举个例子,使用 vuese 生成的markdown文件大致如下:
你可能已经注意到了,在上面的 markdown 文件中包含了很多诸如:
<!-- @vuese:CompName:props:start -->
<!-- @vuese:CompName:props:end -->
之类的注释,它的作用是告诉 vuese 在生成文档时要将生成的 markdown 代码放置在什么位置,如果一个组件还没有对应的markdown文档,则新生成之,否则会在已有的文档基础上更新。这么做的目的是出于真正使用场景的考虑,因为一个组件的文档不可能仅仅包含上图中展示的内容,它还可能包含开发者自己编写的demo,和其他描述内容。这样我们生成文档的时候就不会覆盖掉开发者自己编写的内容,而是将生成的内容插入到指定的位置。
五、规划
目前 vuese 已经实现的特性如下:


另外目前还不支持插件系统,这也在未来的规划当初。并且在我们团队内部已经将其应用在内部的组件库的文档维护,也计划关注 vue3.0 并兼容之。最后 vuese 刚刚发布有诸多不足之处,但是随着后续的更新迭代,它会变得越来越好,也欢迎感兴趣的同学共建。
其他规划:模板支持 pug、插件系统、还有啥??????
转载自:https://juejin.cn/post/6844903698347327495