VuePress 搭建组件库文档

5,905 阅读9分钟

前言

前段时间弄了组件库 demo 以及私有 npm 包托管的一些内容,是时候需要一个在线文档了。也算是走了一个组件库开发的闭环,留下点内容,方便自己也希望能给小伙伴们一些帮助。

基于VueCli开发vue组件库

Verdaccio搭建私有npm仓库

文档方面本次同样使用 vue 相关技术栈 vuepress

vuepress 基于 Vue 的静态网站生成器。它诞生的初衷就是为了支持 Vue 及其子项目的文档需求。

so 用它真是在合适不过了。当然它也可以做一些其他的文档,比如个人博客等... ...

项目预览地址以及项目地址如下:

开始

1,创建 comp-vuepress 项目并执行 npm init -y

2,安装 VuePress 为本地依赖。

npm install vuepress -D

3,根目录下创建 docs 文件夹并在内部创建 README.md 文件,随意输入内容。

4,package.json 添加运行脚本以及打包脚本。

"scripts": {
	"docs:dev": "vuepress dev docs",
	"docs:build": "vuepress build docs"
}

5,运行 npm run docs:dev vuepress 会在 http://localhost:8080 运行服务。

1.png

基本配置

在文档目录下创建一个 .vuepress 的文件夹,所有 VuePress 相关的文件都将会被放在这里。你的项目结构可能是这样:

.
├─ docs
│  ├─ README.md
│  └─ .vuepress
└─ package.json

.vuepress 中创建 config.js 这是 VuePress 中必备的配置文件。应该导出一个对象,内容如下:

module.exports = {
  title: 'comp-vuepress',
  description: 'comp 组件库文档。'
}

导航栏

导航栏 配置主要就两种方式,一级导航和下拉列表导航。修改 config.js

module.exports = {
  // ... ...
  themeConfig: {
    nav: [
      // 一级导航
      { text: '指南', link: '/guide/' },
      // 下拉列表导航
      {
        text: '了解更多',
        items: [
          { text: 'github', link: 'https://github.com/ShuQingX/vue-comp-test', target: '_blank' },
          { text: 'preview', link: 'https://shuqingx.github.io/vue-comp-test/', target: '_blank' }
        ]
      }
    ]
    // 禁用导航,与上面的配置是互斥行为。
    // navbar: false
  }
};

2.png

小插曲(路由)

经过上面的配置相信会有一些小伙伴点击了 “ 指南 ” ,发现 404 了。主要是 /guide/ 我们并没有配置,而 VuePress 遵循 约定优于配置 的原则。我们需要新建一个 guide 目录。

.
├─ docs
│   ├──	README.md
│   ├── guide
│   │   └── README.md
│   └── config.md
└─ package.json

举个🌰:

  • /guide/README.md 的查找规则为 /guide/

  • /guide.md 的查找规则为 /guide.html

README.md 就像 JavaScript 中的 index.js 一样,有没有。

侧边栏

想要使 侧边栏(Sidebar)生效,需要配置 themeConfig.sidebar,基本的配置,需要一个包含了多个链接的数组:

总体来说侧边栏大致分为两大类,一种根据标题生成,一种根据配置生成。根据配置生成的方式比较灵活,强烈建议反复查阅文档。

配置生成侧边栏

1,首先在 guide 中创建 Button.md Card.md 文件。

.
├─ docs
│   ├──	README.md
│   ├── guide
│   │   └── README.md
│   │   └── Button.md
│   │   └── Card.md
└─ package.json

2,修改 config.js 文件

module.exports = {
    themeConfig: {
        sidebar: {
          '/guide/': [
            ['', '介绍'], // '' 等价于 /guide/
            {
              title: '组件',
              collapsable: false,
              children: [
                ['../guide/Button.md', 'Button'],
                ['../guide/Card.md', 'Card']
              ]
            }
          ]
        }
    }
}

3.png

标题生成侧边栏

默认情况下,侧边栏会自动地显示由当前页面的标题(headers)组成的链接,并按照页面本身的结构进行嵌套,你可以通过 themeConfig.sidebarDepth 来修改它的行为。

  • 默认的深度是 1,它将提取到 h2 的标题;

  • 设置成 0 将会禁用标题(headers)链接;

  • 最大的深度为 2,它将同时提取 h2 和 h3 标题。

很显然如果我们直接改配置文件,它就作用到了所有的文件中,此时可以使用 YAML front matter 来为某个页面重写此值:

---
sidebarDepth: 2
---

重写后随意加几个二三级标题,就会得到以下内容

4.png

首页

经过上面的折腾,至此我们的 guide 相关的侧边栏,导航栏都已经完成了。紧接着就搞一个和 VuePress 一样的首页吧。

默认主题的 首页 是提供了一套默认的配置。 但是需要注意使用的前提。

  • 根级的 README.md 中存在。

  • home: true 是必须的。

---
home: true
heroImage: /hero.png   # 注意这里可以使外链,也可以是静态资源
heroText: Hero 标题
tagline: Hero 副标题
actionText: 快速上手 →
actionLink: /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
---

小插曲 (静态资源)

此时当你查看服务的时候,必然会出现图片没有显示的问题。随后把 homeImage 的值进行更改,但前提要在 .vuepress 中新建一个 public 文件夹存储咱们的静态资源。

此时项目的结构应该是下面这样:

.
├─ docs
│  ├── .vuepress
│  │   ├── config.js
│  │   └── public
│  ├── guide
│  │   ├── Button.md
│  │   ├── Card.md
│  │   └── README.md
│  └─	README.md
└─ package.json

此时如果你想使用 public 中的资源,可以直接引用,示例:/hero.png,当然此种方案并不适用静态资源较多的情况,因为它并不会被打包。当然还有 另一种方案 ,这里不展示了。

5.png

小插曲(样式)

此时我们发现图片显示的有点太大了,不够协调。并且我也不想要 Vue 的这种绿色,当然 VuePress 还是提供了方案的。

关于样式,主题颜色的修改,需要在 .vuepress 中新建 styles 文件夹。注意 内部的样式文件是 stylus 文件。

  • 如果想要修改,添加一些样式可以 styles 中新建 index.styl。当然也支持导入其他的 css 文件。参考

  • 如果想要修改一些预设(内置的 css 变量)可以在 styles 中新建 palette.styl注意: 可以修改以下变量

// 颜色
$accentColor = #3eaf7c
$textColor = #2c3e50
$borderColor = #eaecef
$codeBgColor = #282c34
$arrowBgColor = #ccc
$badgeTipColor = #42b983
$badgeWarningColor = darken(#ffe564, 35%)
$badgeErrorColor = #DA5961

// 布局
$navbarHeight = 3.6rem
$sidebarWidth = 20rem
$contentWidth = 740px
$homePageWidth = 960px

// 响应式变化点
$MQNarrow = 959px
$MQMobile = 719px
$MQMobileNarrow = 419px

此时项目的结构应该是下面这样:

.
├─ docs
│  ├── .vuepress
│  │   ├── config.js
│  │   ├── styles
│  │   │	├── index.styl
│  │   │	└── palette.styl
│  │   └── public
│  ├── guide
│  │   ├── Button.md
│  │   ├── Card.md
│  │   └── README.md
│  └─	README.md
└─ package.json

举个🌰:

  • 假设我们想要修改图片的大小。
.home .hero img
  width: 500px
  • 假设我们想要修改一些变量直接在 palette.styl 中进行覆盖即可。

markdown 语法拓展

语法拓展 相信 markdown 的语法,大部分小伙伴都是了解一二的,这里是 VuePress 中的一些拓展语法,大部分语法都是比较简单的,这里重点看下文件导入。

小插曲(components、md in Vue)

文件导入之前我们需要了解一个小知识:

  • docs/.vuepress/components 该目录中的 Vue 组件将会被自动注册为全局组件。换句话说就是在这个文件夹中的 *.vue 是可以在 *.md 中当做组件被使用的。

components/demo-1.vue 在使用时则是 <demo-1></demo-1>

components/guide/demo1.vue 在使用时则是 <guide-demo1></guide-demo1>

  • *.md 中也是会支持 Vue 的模板语法。原因是:Markdown 文件将首先被编译成 HTML,接着作为一个 Vue 组件传入 vue-loader

这意味着你可能会写出以下代码。

6.png

如果不想让它编译,仍然是使用 markdown 拓展语法。

::: v-pre
{{ 1 + 2 }}
:::

代码块导入

假设我们此时要完善 Button 组件的文档。首先创建 guide/button/demo1.js 文件夹存放 demo ,接着随意输入一些代码。然后再 Button.md 中进行导入操作 导入的语法为 <<< @/xxx/xx/x

  • <<< 特定语法

  • @ 的默认值为 process.cwd()

7.png

引入外部组件(应用级别配置)

官方: 由于 VuePress 是一个标准的 Vue 应用,你可以通过创建一个 .vuepress/enhanceApp.js文件来做一些应用级别的配置,当该文件存在的时候,会被导入到应用内部。enhanceApp.js 应该 export default一个钩子函数,并接受一个包含了一些应用级别属性的对象作为参数。你可以使用这个钩子来安装一些附加的 Vue 插件、注册全局组件,或者增加额外的路由钩子等。

// 使用异步函数也是可以的
export default ({
  Vue, // VuePress 正在使用的 Vue 构造函数
  options, // 附加到根实例的一些选项
  router, // 当前应用的路由实例
  siteData, // 站点元数据
  isServer // 当前应用配置是处于 服务端渲染 或 客户端
}) => {
  // ...做一些其他的应用级别的优化
}

上面的内容告诉我们可以使用插件,这是写组件库文档必不可少的,刚好符合需求,接下来就按照官方的提示改造 enhanceApp.js

文档就用 element-ui 作为示例(反正之前的组件库也是基于它来做的不冲突)。

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

export default ({ Vue, options, router, siteData, isServer }) => {
  Vue.use(ElementUI);
};

然后在 Button.md 中使用组件。

<el-button type="primary" size="mini">按钮</el-button>

那不出意外还是出意外了。

10.png

总体来说就是依赖不一致导致的问题,有兴趣的可以看下。解决方案 如下:

npm install async-validator@1.11.5 

8.png

插件

插件 可以使用官方或者社区的插件,也可以根据自己的需求开发适合自己的插件。开发插件

安装

npm install -D @vuepress/plugin-back-to-top

插件使用

// config.js
module.exports = {
  // ... ...
  plugins: ['@vuepress/back-to-top']
}

再推荐小伙伴们安装一个非官方插件,用于代码预览 vuepress-plugin-demo-container 如何使用?

组件库文档编写

小伙伴去 element 的 Button 把里面的文档复制下来放到咱们自己的 Button.md 中。

看到最后的预览图可能有些小伙伴会觉得按钮都有点挤,内容区域有点窄等等样式问题,index.styl palette.styl 帮你解决问题。

QQ20220214-153524-HD.gif

国际化

如果要实现国际化配置,项目结构应该是下面这样。 接着把 en/guide/Button.md 注入英文文档

.
├─ docs
│  ├── .vuepress
│  │   ├── config.js
│  │   ├── styles
│  │   │	├── index.styl
│  │   │	└── palette.styl
│  │   └── public
│  ├── en
│  │   ├── guide
│  │   │   ├── Button.md
│  │   │   ├── Card.md
│  │   │   └── README.md
│  │   └─README.md
│  ├── guide
│  │   ├── Button.md
│  │   ├── Card.md
│  │   └── README.md
│  └─	README.md
└─ package.json

config.js 中提供了 locales 选项:

注意 locales.title locales.description 的优先级会高于 title description,同时页面的右上角会出现切换语言的下拉框。

module.exports = {
  locales: {
    '/': {
      lang: 'zh-CN',
      title: 'VuePress',
      description: 'Vue 驱动的静态网站生成器'
    },
    // 键名是该语言所属的子路径
    // 作为特例,默认语言可以使用 '/' 作为其路径。
    '/en/': {
      lang: 'en-US', // 将会被设置为 <html> 的 lang 属性
      title: 'VuePress',
      description: 'Vue-powered Static Site Generator'
    }
  }
}

然后我们还需要把 导航栏,侧边栏,以及文档的路由做出修改。这些操作都将在 themeConfig.locales 中进行。

注意 当配置此选项时,原有的 themeConfig.nav themeConfig.sidebar 更改为 themeConfig.locales.nav themeConfig.locales.sidebar

modeule.exports = {
  themeConfig {
    locales: {
      '/': {
        selectText: '选择语言', // 页面右上角选择语言label
        lebel: '简体中文', // 页面右上角选择语言下拉框 value
	nav: [
          { text: '指南', link: '/guide/' },
          // ...
        ],
        sidebar: {
          '/guide/': [
            ['', '介绍'],
            {
              title: '组件',
              collapsable: false,
              children: [
                ['../guide/Button.md', 'Button'],
                ['../guide/Card.md', 'Card']
              ]
            }
          ]
        }
      },
      // 配置 英文时注意路由的更改
      '/en/': {
        selectText: 'Languages', // 页面右上角选择语言label
        lebel: 'English', // 页面右上角选择语言下拉框 value
	nav: [
          { text: 'guide', link: '/en/guide/' },
          // ...
        ],
        sidebar: {
          '/en/guide/': [
            ['', 'Guide'],
            {
              title: 'Components',
              collapsable: false,
              children: [
                ['../guide/Button.md', 'Button'],
                ['../guide/Card.md', 'Card']
              ]
            }
          ]
        }
      }
    }
  }
}

12.gif

发布

打包

忙活了大半天终于打包了。执行之前配置的打包命令 npm run docs:build。可能有些小伙伴打包环节会遇到以下错误。

13.png

原因就是 静态HTML 是通过 Node 渲染,咱们在注册 element 组件时机不太行。可以将 enhanceApp.js 修改成如下:

import 'element-ui/lib/theme-chalk/index.css';

export default async ({ Vue, options, router, siteData, isServer }) => {
  if (!isServer) {
    await import('element-ui').then(ElementUI => {
      Vue.use(ElementUI);
    });
  }
};

打包完成之后的文件在 /docs/.vuepress/dist

发布到 githubPages

1,登录到 github 创建 Repository

14.png

2,在 config.js 中新增 base 选项:值为创建仓库时的 Repository name

modeule.exports = {
  base: '/comp-vuepress/'
}

3,根目录新建 deploy.sh 文件

#!/usr/bin/env sh

# 确保脚本抛出遇到的错误
set -e

# 生成静态文件
npm run docs:build

# 进入生成的文件夹
cd docs/.vuepress/dist

# 如果是发布到自定义域名
# echo 'www.example.com' > CNAME

git init
git add -A
git commit -m 'deploy'

# 如果发布到 https://<USERNAME>.github.io/<REPO> 这里做出对应的替换
# git push -f git@github.com:<USERNAME>/<REPO>.git master:gh-pages

cd -

3,package.json 中新增一条脚本, 并执行

"scripts": {
    "docs:deploy": "bash deploy.sh"
}

4,随后去 github 查看会多出一个 gh-pages 的分支。然后点击 Setting 进行设置

15.png

5,随后等个两三分钟,刷新下页面,链接变成绿色即可点击啦!

16.png