使用Vue Styleguidist编写组件文档

在日常 Vue 项目开发中,我们难免需要写一些基础公共组件,在大型项目中,我们难免会遇到下面的痛点:

  • 组件 API,对于 Vue 组件,有 props、event、slot 等接口,在团队内需要一定的沟通成本
  • 对于 UI 组件,还需要提供预览,方便团队内快速选择合适的组件

如果使用 Markdown 撰写,虽然能写 API 文档,但是无法提供组件预览,并且手动写文档的成本也很大

好在有一个这样的库,可以提供自动化文档的生成,并提供组件预览,这个库叫做 Vue Styleguidist

安装

首先,Vue Styleguidist 只能适用于 Webpack 打包的项目,在此基础上,我们需要安装 vue-styleguidist 这个包

npm install vue-styleguidist --save-dev

然后在 package.json 配置下面两行命令,分别用于开发预览和部署打包

{
  "scripts": {
    "styleguide": "vue-styleguidist server",
    "styleguide:build": "vue-styleguidist build"
  }
}

如果是使用 @vue/cli 3 生成的项目,可以直接使用 vue-cli-plugin-styleguidist 这个插件进行更快捷的安装和配置

组件源代码

这里为了进行简单的演示,我们使用了 @vue/cli 3 生成项目,创建组件 src/components/AppButton/AppButton.vue

我们的组件源代码如下,下面的案例都会依照此源代码进行展开

<template>
  <button
    class="btn"
    :type="htmlType"
    :class="btnClass"
    :disabled="disabled"
    @click="handleClick"
  >
    <slot />
  </button>
</template>

<script>
export default {
  name: 'AppButton',
  props: {
    theme: {
      type: String,
      required: true,
    },
    outline: {
      type: Boolean,
      default: false,
    },
    size: {
      type: String,
      default: '',
    },
    block: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    htmlType: {
      type: String,
      default: 'button',
    },
  },
  computed: {
    btnClass() {
      return {
        [`btn-${this.theme}`]: !!this.theme && !this.outline,
        [`btn-outline-${this.theme}`]: this.outline,
        [`btn-${this.size}`]: !!this.size,
        'btn-block': this.block,
      };
    },
  },
  methods: {
    handleClick(e) {
      this.$emit('click', e);
    },
  },
};
</script>

<style lang="scss">
@import '~bootstrap/scss/bootstrap';
</style>

Props

Props 是组件最基本的 API,用于为组件传递数据

实际上,在配置好 Vue Styleguidist 之后,如果有写 prop,就已经能生成一个这样的文档

我们可以看到,此时已经带出了 Props 的名称、参数类型、默认值、必填性等等

我们仅仅需要为 prop 写上相应的注释进行描述,像 JSDoc 一样,就能生成一份完整的 Props 说明

export default {
  props: {
    /**
     * 按钮主题,有效值:
     */
    theme: {
      type: String,
      required: true,
    },
    /**
     * 启用outline样式
     */
    outline: {
      type: Boolean,
      default: false,
    },
    /**
     * 按钮大小,有效值:lg sm
     */
    size: {
      type: String,
      default: '',
    },
    /**
     * 启用块状按钮
     */
    block: {
      type: Boolean,
      default: false,
    },
    /**
     * 禁用状态
     */
    disabled: {
      type: Boolean,
      default: false,
    },
    /**
     * 按钮类型,有效值:button submit reset
     */
    htmlType: {
      type: String,
      default: 'button',
    },
  },
};

最终会得到一份完整的 Props 说明

Events

除了 Props,Event 事件也是 Vue 的一个重要的 API 之一,可以通过 v-on 为组件绑定事件

Vue 的事件使用 vm.$('event', ...params) 的方法进行定义,我们只需要在这个方法之前,加上必要的注释就可以了

  • 如果事件名不是字符串,可以使用 @event 进行标注
  • 事件的参数使用 @type 进行标注
export default {
  methods: {
    handleClick(e) {
      /**
       * 单击事件
       * @type {Event}
       */
      this.$emit('click', e);
    },
  },
};

Slots

Slot 插槽是 Vue 的自定义元素之一,Slot 向一个组件传递内容,也是封装公共组件常见的 API 之一

与 Props 和 Events 不同的是,Slots 通常是定义在 <template> 部分,不能使用 JS 注释进行标注,需要使用 HTML 注释,并且在注释里使用 @slot 进行标注

<button
  class="btn"
  :type="htmlType"
  :class="btnClass"
  :disabled="disabled"
  @click="handleClick"
>
  <!-- @slot 按钮的内容 -->
  <slot />
</button>

如果使用的是具名 Slot(具名插槽),则会自动生成插槽名称

<button
  class="btn"
  :type="htmlType"
  :class="btnClass"
  :disabled="disabled"
  @click="handleClick"
>
  <!-- @slot 按钮的图标 -->
  <slot name="icon" />
  <!-- @slot 按钮的内容 -->
  <slot />
</button>

Methods

看到这里可能各位会有个疑问,Methods 和 Events 有什么区别?

区别主要有以下两个:

  • 定义方式不同:Methods 只要在 methods 里定义函数即可,Events 则需要使用 vm.$('event', ...params) 进行定义
  • 调用方式不同:Methods 使用 vm.$refs.ref.method() 这样的方式进行调用,Events 使用 v-on 指令或者 vm.$on('event') 进行监听

实际上,使用 Methods 方法封装组件 API 的情况是比较少的,但是依然不能排除这种情况

对于 Methods 方法,我们只需要像使用 JSDoc 一样为函数进行注释就可以了,最后再附上 @public 进行标识

export default {
  methods: {
    /**
     * 单击事件
     * @param {Event} e
     * @public
     */
    click(e) {
      // some code
    },
  },
};

编写样例

通过上面的例子,我们将至少得到一个这样的文档

这个文档把该有的 API 都暴露出来了,但是它并不是完美无缺的:

  • 直接对比 Markdown 文档,并没有明显的优势
  • 缺乏组件 UI 样例

在文章的开头,我们说过,UI 组件需要为团队提供样式预览,并且需要自动生成

很遗憾的是,Vue Styleguidist 并不能直接生成预览样式,这个需要手写样式代码

在 Vue Styleguidist,有两种编写组件样例的方式

README方式

在组件的同个目录下,新建一个文件 README.md,比如 src/components/AppButton/README.md

然后直接调用组件代码

这样我们就能快速简洁的生成一个预览+示例代码了

我们也可以为组件传入 Props,预览组件的表现

<docs>组件方式

可以直接在组件下方增加一个组件 <docs></docs>,并在里面直接使用 Markdown 编写样例,这种方式适用于项目结构比较简单的项目,但是我认为会使组件的代码变得很冗长,在此不再赘述

配置

Vue Styleguidist 支持自定义配置,只需要在项目根目录下,创建 styleguide.config.js,就可以参照官方文档进行配置

// styleguide.config.js
module.exports = {
  title: 'Default Style Guide',          // 文档的标题
  components: 'src/components/**/*.vue', // 组件的目录
  defaultExample: false,                 // 是否使用默认样例
  usageMode: 'expand',                   // 是否展开用法
  exampleMode: 'expand',                 // 是否展开示例代码
  styleguideDir: 'styleguide',           // 打包的目录
  codeSplit: true,                       // 打包时是否进行分片
  skipComponentsWithoutExample: true,    // 是否跳过没有样例的组件
};

注意事项和总结

注意事项

  • JSDoc 的标签仍然有效
  • TypeScript、Flow 和 Class 组件同样可以使用,只是使用方法稍有不同
  • JSX 也可以使用

总结

通过 Vue Styleguidist,我们将至少得到下面两项好处:

  • 完整的组件 API 和用法
  • 直观的组件预览

另外我认为还能得到以下几项好处:

  • 完善的注释
  • 较小的 UI 调试成本
  • 减少团队内部沟通成本

Vue Styleguidist 实际上还拥有多种配置和用法,各位感兴趣可以去官网看看文档

题外话:实际上 Vue Styleguidist 来源于 React Styleguidist(也依赖于这个项目),使用方法大同小异,不同于 Vue 组件的是,React 组件的 API 只有 Props 一项

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容