使用Vuepress搭建基于Vue 3.2的组件库(保姆级教程)

7,541 阅读1分钟

大家好我是来蹭饭,一个会点儿吉他和编曲,绞尽脑汁想傍个富婆的摸鱼大师。

今天跟大家分享使用Vuepress2.0 + Vue3.2 + Ant来搭建自己的组件库文档。

一. 前言

众所周知Vue已经来到了3.2的版本,使用<script setup>的语法糖给开发者带来了极大的便利。源于对Vue高版本的支持,本次使用Vuepress 2.0开发组件库文档,需求如下:

  • 组件库能直接引用或二次封装Ant的组件
  • 组件库能显示组件代码供他人查阅

案例完成后,你将能看到这样婶儿的效果:

1.png

下面我将从安装,页面搭建,组件的引入与封装等章节跟大家分享组件库的搭建过程。废话少说,大家坐稳扶好,我们发车。

二. 安装

2.1 初始化

Vuepress 2.0对Node的版本支持为Node.js V12+,各单位注意自行升级Node的版本。

安装非常简单,cd到项目目录下然后一条命令即可

  • 步骤1: 创建项目
yarn add -D vuepress@next

安装完毕后我们会发现项目变成这样了:

3.png

纳尼?

4.jpg

怎么只有一个node_modules?这跟我们用cli梭出来的项目差异这么大吗?我反复确认了安装命令已经完成,才敢继续下面的操作。

  • 步骤2:package.json中添加如下命令
{
  "scripts": {
    "docs:dev": "vuepress dev docs",
    "docs:build": "vuepress build docs"
  }
}
  • 步骤3: 启动项目(完成页面搭建章节的config.js配置README.md配置后启动)
yarn docs:dev

2.2 项目结构

Vuepress是用来写文档的,所以它的所有文件都会被包裹在docs文件夹下(可以理解成Vuepress项目的src文件夹)。你可以在自己的Vuepress项目中单独创建docs也可以让它“寄生”在已经存在的项目下(与原项目的运行互不影响):

5.png

本次案例我们以单独创建的Vuepress项目为例进行讲解。

经过我们的DIY后,docs文件夹初始化后的目录结构如下:

├─ docs
│  ├─ .vuepress
│  │  └─ config
│  │  └─ public
│  │  └─ config.js
│  └─ README.md
├─ .gitignore
└─ package.json

上述文件的作用分别是:

  • docs/.vuepress: 存放全局的配置、组件、静态资源等。
  • docs/.vuepress/config: 存放页眉导航栏和子页面侧栏的配置文件。
  • docs/.vuepress/public: 静态资源目录。
  • docs/.vuepress/config.js: 配置文件的入口文件。
  • docs/README.md: 文档的入口页面。

至此安装步骤算是完成了,接下来进入页面搭建的环节。完成后,我们就可以启动项目查看自己的文档了。

三. 页面搭建

写文档的过程就像写书,书本的每一页即为读者看到的内容。在Vuepress中md文件就是书的页面,也是对应路由的结果。接下来我们通过配置config.js文件,md文件,页眉导航栏navbar以及页面侧栏sidebar来完成页面搭建。

在开始配置前我们先把目录结构调整如下:

├─ docs
│  ├─ .vuepress
│  │  └─ config
│  │  │  └─ navbar.js
│  │  │  └─ sidebar.js
│  │  └─ public
│  │  │  └─ img
│  │  └─ config.js
│  └─ README.md
├─ .gitignore
└─ package.json

3.1 config.js配置

themeConfig中logo寻址的路径默认是public文件夹,将对应的logo图片放入img文件夹中。

module.exports = {
  // 站点配置
  lang: 'zh-CN',
  title: 'UI-LIB',
  description: '组件库',
  head: [ // 注入到当前页面的 HTML <head> 中的标签
    ['link', {
      rel: 'icon',
      href: '/img/logo.png'
    }], // 增加一个自定义的 favicon(网页标签的图标)
  ],
  // 主题和它的配置
  theme: '@vuepress/theme-default',
  themeConfig: {
    logo: '/img/logo.png',
  }
}

3.2 入口README.md配置

---
home: true
heroImage: /img/logo.png
actionText: 快速上手 
actionLink: /zh/`guide/
features:
- title: 简洁至上
  details:  Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
- title: Vue驱动
  details: 享受 Vue + webpack 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
- title: 高性能
  details: VuePress 为每个页面预渲染生成静态的 HTML,同时在页面被加载的时候,将作为 SPA 运行。
footer: MIT Licensed | Copyright © 2018-present Evan You
---

当完成上述配置后,通过yarn docs:dev运行项目,此时项目会新增.cache.temp文件,不用管它们,直接看运行效果:

6.png

一个最基础的文档页面就诞生了!接下来我们进入导航栏侧栏的配置。

3.3 navbar.js 导航栏配置

导航栏的配置,是配置二级页面路由的过程(外链导航除外),我们希望导航栏实现这样的效果:

  • 导航栏包含组件,文档,工具箱三个选项
  • 点击组件,能路由到组件页面
  • 鼠标经过文档时,弹出二级菜单选项,点击直接进入二级菜单页
  • 工具箱的展开内容是一些线上地址的链接,点击后跳转线上页面

8.png

接下来我们逐步实现上述需求:

  • 步骤1: 调整项目结构,在docs中添加componentsdocument两个文件夹,具体内容如下:

7.png

  • 步骤2: 将navbar.js文件引入config.js中
const navbar = require('./config/navbar')

module.exports = {
  // 站点配置
  lang: 'zh-CN',
  title: 'UI-LIB',
  description: '组件库',
  head: [ // 注入到当前页面的 HTML <head> 中的标签
    ['link', {
      rel: 'icon',
      href: '/img/logo.png'
    }], // 增加一个自定义的 favicon(网页标签的图标)
  ],
  // 主题和它的配置
  theme: '@vuepress/theme-default',
  themeConfig: {
    logo: '/img/logo.png',
    navbar,
    sidebarDepth: 2, // 侧边栏显示2级
  }
}
  • 步骤3:docs/.vuepress/config/navbar.js中做出如下配置:
module.exports = [{    text: '组件',    link: '/components/'  },  {    text: '文档',    children: [{        text: '介绍',        link: '/document/introduction/'      },      {        text: '注意事项',        link: '/document/tips/'      },    ]
  },
  {
    text: '工具箱',
    children: [{
        text: '在线编辑',
        children: [{
          text: '图片压缩',
          link: 'https://tinypng.com/'
        }]
      },
      {
        text: '在线服务',
        children: [{
            text: '阿里云',
            link: 'https://www.aliyun.com/'
          },
          {
            text: '腾讯云',
            link: 'https://cloud.tencent.com/'
          }
        ]
      },
      {
        text: '博客指南',
        children: [{
            text: '掘金',
            link: 'https://juejin.im/'
          },
          {
            text: 'CSDN',
            link: 'https://blog.csdn.net/'
          }
        ]
      }
    ]
  }
]

这里讲解一下各字段的含义

  • text: 显示在导航栏上的文字信息

  • link: 路由地址显示的页面(类似于router中的component)会自动寻址到指定目录下的README.md,把它作为主入口显示出来。link字段也可以配置线上地址,点击后跳转到线上页面。默认页面路由地址如下:

    文件的相对路径页面路由地址
    /README.md/
    /components/README.md/components/
  • children: 子路由的嵌套信息,可多层嵌套,上述示例已给出

工具箱配置为例,效果如图所示,结合代码更好理解:

9.png

至此我们的导航栏配置工作完成。

由此可见,导航栏上每一个能进入二级页面的导航,都要对应一个页面文件:

导航栏选项对应文件
组件components
文档/介绍document/introduction
文档/注意事项document/tips

现在进入组件页面,里面还是一片空白。接下来我们通过配置sidebar.js,实现二级页面的侧栏切换功能。

10.png

3.4 sidebar.js 侧栏配置

侧栏的承载的信息比较多,因此我们在docs/.vuepress/config/sidebar.js中配置整合后的侧栏信息。每个页面的侧栏交由该页面独立控制,以components页面为例进行侧栏配置。

  • 步骤1: config.js引入侧栏并使用
const navbar = require('./config/navbar')
const sidebar = require('./config/sidebar')

module.exports = {
  // 站点配置
  lang: 'zh-CN',
  title: 'UI-LIB',
  description: '组件库',
  head: [ // 注入到当前页面的 HTML <head> 中的标签
    ['link', {
      rel: 'icon',
      href: '/img/logo.png'
    }], // 增加一个自定义的 favicon(网页标签的图标)
  ],
  // 主题和它的配置
  theme: '@vuepress/theme-default',
  themeConfig: {
    logo: '/img/logo.png',
    navbar,
    sidebar,
    sidebarDepth: 2, // 侧边栏显示2级
  }
}
  • 步骤2:config/sidebar.js中导出配置
module.exports = {
  '/components/': require('../../components/sidebar'),
}
  • 步骤3: 配置components文件,结构修改如下:

    pages:当前页面下的子路由,目前新增了ButtonInputNumberSlider3个子页面

    sidebar.js:当前页面的侧栏配置

    11.png

  • 步骤4: 配置components文件中的sidebar.js:

module.exports = [{
    text: '通用',
    collapsable: true,
    children: [{
      text: 'Button 按钮',
      link: '/components/pages/Button',
    }]
  }, {
    text: '数据录入',
    collapsable: true,
    children: [{
      text: 'Slider 滑动组件',
      link: '/components/pages/Slider',
    }]
  },
  {
    text: '数字输入框',
    collapsable: true,
    children: [{
      text: 'InputNumber 数字输入框',
      link: '/components/pages/InputNumber',
    }]
  },
]

sidebar中的字段与navbar类似,这里不多做赘述,配置完成后就能正常点击components下的侧栏了。

12.png

至此页面搭建工作完成!接下来将Ant引入文档中,进一步完善我们的组件库。

四. 组件的引入与封装

本章节需要解决以下两个问题:

  • Ant引入Vuepress后如何注册并使用?
  • 二次封装组件时,我们希望在.vue文件中操作,之后使用md文件引入.vue文件。如何让md文件正确识别.vue文件?

下面我将逐步讲解这两个问题的解决方式。

4.1 引入Ant

  • 步骤1: 修改.vuepress文件夹结构,新增clientAppEnhance.js文件(迁移特性Vuepress1中名称为enhanceApp.js),该文件的作用是增强客户端应用
├─.vuepress
│  └─ config
│  │  └─ navbar.js
│  │  └─ sidebar.js
│  └─ public
│  │  └─ img
│  └─ clientAppEnhance.js
│  └─ config.js
  • 步骤2: 安装Ant

yarn add ant-design-vue@next

  • 步骤3: 配置clientAppEnhance.js文件
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'


import { defineClientAppEnhance } from '@vuepress/client'

export default defineClientAppEnhance(({ app, router, siteData }) => {
  app.use(Antd)
})

到这里使用Ant的前置工作已经完成。

4.2 识别.vue文件

  • 步骤1: 安装@vuepress/plugin-register-components插件,在Vuepress1.0中,md文件能自动识别导出的.vue文件,Vuepress2.0中需要安装插件并做好配置

yarn add @vuepress/plugin-register-components@next

  • 步骤2: config.js中配置修改如下:
const {
  path
} = require('@vuepress/utils')
const navbar = require('./config/navbar')
const sidebar = require('./config/sidebar')

module.exports = {
  // 站点配置
  lang: 'zh-CN',
  title: 'UI-LIB',
  description: '组件库',
  head: [ // 注入到当前页面的 HTML <head> 中的标签
    ['link', {
      rel: 'icon',
      href: '/img/logo.png'
    }], // 增加一个自定义的 favicon(网页标签的图标)
  ],
  // 主题和它的配置
  theme: '@vuepress/theme-default',
  themeConfig: {
    logo: '/img/logo.png',
    navbar,
    sidebar,
    sidebarDepth: 2, // 侧边栏显示2级
  },
  plugins: [
    [
      '@vuepress/plugin-register-components',
      {
        componentsDir: path.resolve(__dirname, './components')
      }
    ],
  ]
}
  • 步骤3:.vuepress文件夹中新建components文件夹,里面存放vue组件,文件结构如下:
├─.vuepress
│  └─ components
│  │  └─ Button.vue
│  └─ config
│  │  └─ navbar.js
│  │  └─ sidebar.js
│  └─ public
│  │  └─ img
│  └─ clientAppEnhance.js
│  └─ config.js

至此md文件就能无需引入即可自动识别.vuepress/components/下所有的vue组件了。继续完成下面的步骤,预览效果。

  • 步骤4: Button.vue文件添加Ant中Button的代码:
<template>
  <a-button type="primary">Primary Button</a-button>
  <a-button>Default Button</a-button>
  <a-button type="dashed">Dashed Button</a-button>
  <a-button type="text">Text Button</a-button>
  <a-button type="link">Link Button</a-button>
</template>
  • 步骤5: docs/components/pages/Button.md文件直接引入Button.vue
常用的操作按钮。

## 基础用法

基础的按钮用法。


<Button />

至此组件库初具雏形,我们看看效果:

13.png

现在还剩一些问题待解决:

  • 如果组件业务逻辑不复杂,比如Button.vue中只有template代码,并不包含业务逻辑功能。这种情况下能否直接将.vue文件的内容写在md文件里?
  • 如何在组件底部添加"显示代码"的按钮,供他人查阅组件代码?

我们通过本文最后的part来解决这些问题。

4.3 直接书写vue语法

上述问题可以通过安装vuepress-plugin-demoblock-plus插件,再配置config.js解决。

  • 步骤1: 安装插件。

yarn add vuepress-plugin-demoblock-plus -D

  • 步骤2: 修改config.js中plugins的配置:
plugins: [
    [
      '@vuepress/plugin-register-components',
      {
        componentsDir: path.resolve(__dirname, './components')
      }
    ],
    [      'vuepress-plugin-demoblock-plus'    ]
 ]
  • 步骤3: 修改docs/components/pages/Button.md文件:
# Button 按钮

常用的操作按钮。

## 基础用法

基础的按钮用法。

:::demo 使用`type`、`plain`、`round`和`circle`属性来定义 Button 的样式。

```vue
<template>
  <a-button type="primary">Primary Button</a-button>
  <a-button>Default Button</a-button>
  <a-button type="dashed">Dashed Button</a-button>
  <a-button type="text">Text Button</a-button>
  <a-button type="link">Link Button</a-button>
</template>
```(避免转义,使用时去掉整个括号的内容)

:::

我们看看预览效果:

14.png

到这一步组件库的基本功能就实现了。接下来我们简单封装一个自己的InputNumber组件。

4.4 展示二次封装的业务组件

  • 步骤1: 更改.vuepress文件夹结构:
├─.vuepress
│  └─ components
│  │  └─ InputNumber.vue
│  └─ config
│  │  └─ navbar.js
│  │  └─ sidebar.js
│  └─ public
│  │  └─ img
│  └─ clientAppEnhance.js
│  └─ config.js
  • 步骤2: InputNumber.vue文件添加如下代码:
<template>
  <a-input v-model:value="value" placeholder="Basic usage" />
</template>
<script setup>
import { ref, defineProps } from "vue";

const props = defineProps({
  defaultValue:String
})
const value = ref(props.defaultValue);

</script>
  • 步骤3: 修改docs/components/pages/InputNumber.md文件:
# Input 输入

常用的操作按钮。

## 基础用法

基础的输入框用法。

:::demo 

```vue
<template>
  <InputNumber :defaultValue="defaultValue"/>
</template>
<script setup>
import { ref } from 'vue';

const defaultValue = ref('来蹭饭')
</script>
```(避免转义,使用时去掉整个括号的内容)

:::

这样一个简单封装的组件就完成了,InputNumber.vue接收到了从InputNumber.md传入的参数,来看看最终的预览效果:

16.png

大功告成!通过上述方法我们就可以为所欲为地搭建自己的组件库了。

五. 尾巴

读到这里你也不易,写到这里我也不易。不嫌麻烦的话欢迎点赞,评论,收藏,你们的支持是我创作的最大动力。

我是来蹭饭,一个会点儿吉他和编曲,绞尽脑汁想傍个富婆的摸鱼大师,希望本次的分享对你有帮助。

打完收工,纪念一下掘金的第一篇文章。

end.jpg