你的.vue文件就已是你的文档了

昨天发布了vuese1.0,这是个人一个新的开源项目,用来解析Vue SFC并生成markdown文档,这里: github.com/HcySunYang/…html

这篇文章不会介绍如何使用,至于如何使用你们能够查看 readme,这里咱们主要说一说实现的思路。vue

1、动机

你或者你的团队也许会有一套本身的组件库(或者是单纯的一个组件),一般当你开发完一个组件以后,你须要手动的编写markdown文档,从而让其余人了解组件是如何使用的。这里的问题在于:倘若组件不停的在更新,你就必需要不停的手动维护对应的文档,使其与组件的功能保持一致。实际上这个过程是有些恶心🤢的,那么有什么更好的办法吗?基于此我才开发了 vuesenode

2、想法

咱们知道一个 vue 组件所暴露的接口无非是 propseventsslots(或scopedSlots) 以及部分 methods。那咱们可不能够实现一个工具帮助咱们分析一个vue组件并提取这些信息呢?而后自动生成文档,这样不管组件如何改动咱们都不须要手动维护文档了,只须要使用该工具从新生成便可。git

3、基本思路

对于一个 vue 组件,若是咱们抛开 style 和自定义块,那么它由两部分组成,即:模板 和 script 块。甚至若是使用 render 函数代替模板的话,那么就只剩一下一个 script 了。github

对于 propsmethods 它们只能在 scirpt 块内定义,如:babel

props: {
  name: String
}
methods: {
  clear () {/*...*/}
}
复制代码

而对于 slots 则既能够在 script 块内定义,也能够在模板中定义,如:markdown

<!-- 在模板中定义 -->
<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信息时须要考虑在内的。ui

一样的,对于 events 而言也是既能够出如今模板中,又能够出如今 script 块中,以下:

<!-- 模板中的 events -->
<div @click="$emit('onclear')"></div>
复制代码
// 定义在 script 中的事件
methods: {
  someMethod () {
    this.$meit('onclear')
  }
}
复制代码

因此在提取 events 信息时也须要即考虑模板又考虑 script 块。

对于模板咱们默认是 html 语法,对于 script 块咱们默认为 js。咱们要作的第一件事儿就是将 htmljs 单独提取出来并单独分析,好在巨人的肩膀厚实,已经有了 @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/…

4、生成目标

通过上一步的处理,咱们能够编写出一个 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,和其余描述内容。这样咱们生成文档的时候就不会覆盖掉开发者本身编写的内容,而是将生成的内容插入到指定的位置。

5、规划

目前 vuese 已经实现的特性以下:

规划中要实现的特性以下:

另外目前还不支持插件系统,这也在将来的规划当初。而且在咱们团队内部已经将其应用在内部的组件库的文档维护,也计划关注 vue3.0 并兼容之。最后 vuese 刚刚发布有诸多不足之处,可是随着后续的更新迭代,它会变得愈来愈好,也欢迎感兴趣的同窗共建。

其余规划:模板支持 pug、插件系统、还有啥??????

相关文章
相关标签/搜索