手摸手带你封装Vue组件库(3)组件库文档搭建

2,863 阅读7分钟

接上回我们搭建完组件库项目,现在还剩下组件文档,因为从 0 开始搭建文档太费时间,我推荐大家使用vitepress进行快速搭建,它可以将你的 Markdown 转为文档页面,所以我们只需要写 md 文件既可快速实现文档开发。它自带了很多样式,如果你不喜欢,可以自定义主题,自定义主题的东西比较多,你也可以查询官网相关文档进行学习。

文档目录结构创建

上节我们在根目录下创建了一个 docs 的文件夹,我们接下来需要在这个文件夹下创建我们的组件库文档的项目。

我们在 docs 目录下打开终端,输入以下命令:

npx vitepress init

一直往下进行,你就会得到这样的文件结构:

project-20240812-1.png

我们在组件库根目录下安装一下 vitepress 依赖

pnpm i vitepress -D -w

然后我们需要在根目录package.json中添加一下 vitepress 项目运行命令:

{
  "name": "test-ui",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "vite examples",
    "test": "echo \"Error: no test specified\" && exit 1",
    "docs:dev": "vitepress dev docs", // 文档dev环境运行
    "docs:build": "vitepress build docs", // 文档打包
    "docs:preview": "vitepress preview docs" // 文档预览
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@test-ui/components": "workspace:^",
    "@test-ui/examples": "workspace:^"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.1.2",
    "vite": "^5.3.5",
    "vitepress": "^1.3.2"
  }
}

执行运行命令,运行看一下是否能正常启动

npm run docs:dev

project-20240812-2.png

这就表明已经成功运行了,说一下重点的文件

├─ docs // 站点项目根目录
│  ├─ .vitepress // VitePress 配置文件、开发服务器缓存、构建输出和可选主题自定义代码的位置
│  │  └─ config.js // 配置文件
│  ├─ api-examples.md
│  ├─ markdown-examples.md
│  └─ index.md // 默认首页
└─ package.json

接下来我们需要导入我们组件进行注册。

组件的注册以及使用

要在组件库中显示出组件,我们必须得将我们开发的组件库安装到这个 vitepress 项目中,那我们怎么去安装呢?我们可以通过自定义主题来对当前的项目进行安装任何包,包括我们的组件库。

根据官网所说,我们需要在.vitepress文件夹下创建一个 theme 文件夹,在该文件夹下创建一个 index.js 或者 index.ts 文件作为主题入口文件。

.
├─ docs                # 项目根目录
│  ├─ .vitepress
│  │  ├─ theme
│  │  │  └─ index.js   # 主题入口
│  │  └─ config.js     # 配置文件
│  └─ index.md
└─ package.json

写入以下内容:

import DefaultTheme from "vitepress/theme";
import TestUI from "@test-ui/components";

export default {
  ...DefaultTheme,
  enhanceApp: async ({ app, router, siteData }) => {
    app.use(TestUI);
  },
};

enhanceApp 对应的函数入参中 app 代表当前 vue 实例,意味着我们可以使用它进行注册组件、定义指令等等。

我们清空一下默认带的markdown-examples.md,直接写入我们的组件,看看是否成功渲染。

project-20240812-3.png

好耶 🎉 看来是已经成功渲染了,由于 vitepress 有默认的样式,我们的 button 样式已经被重置了,我们修改一下组件样式来看看

目前我用的是 less 预编译器,这个可以根据个人喜好来选择你的组件库 css 预编译器 根目录下安装 pnpm i less less-loader -D -w 在 theme-chalk 文件夹下创建 index.less 文件作为组件库样式入口文件,后续我们编写组件的时候再具体划分结构

我们在 theme-chalkindex.less 文件中修改一下我们的 button 组件样式,并在文档注册我们组件的地方导入我们组件的样式文件。

.t-button {
  display: inline-block;
  border: 1px solid #ddd;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 14px;
  background-color: #409eff;
  color: #fff;
}
import DefaultTheme from "vitepress/theme";
import TestUI from "@test-ui/components";
import "@test-ui/theme-chalk/index.less"; // 引入样式文件

export default {
  ...DefaultTheme,
  enhanceApp: async ({ app, router, siteData }) => {
    app.use(TestUI);
  },
};

结果如下:

project-20240812-4.png

组件源码预览

在做这个源码预览的时候我也找过相关别人做的一些插件,暂时没有找到什么特别好用的,所以打算自己写,那就带大家来写一个组件源码预览的组件吧。

首先我们先去想怎么去实现,组件预览一般上面是组件的展示,下面有个按钮可以展开查看源码,也就是上面的组件预览我可以作为一个插槽,直接传入我要展示的组件,下面源码就是我这个组件实现的代码,是一整个代码字符串,但是每个组件演示都需要我写这么多字符串给传入进去,是不是很麻烦,既然显示的代码就是我传入插槽的组件,那我可不可以直接将某一个示例的代码写为一个 vue 文件,这个文件既可以当做一个组件传入预览组件的插槽,又可以直接读取这个示例的 vue 文件中的字符串进行展示,这样就完美解决了我的示例代码需要重新写一次的问题。

好了,现在问题还有两个:

  1. 怎么读取 vue 文件中的字符串?
  2. 怎么更好的排版以及高亮我们示例代码?

我直接公布答案:

  1. vite 中资源可以使用 ?raw 后缀声明作为字符串引入,vite 官网文档:将资源引入为字符串
  2. highlight.js 互联网上最受欢迎的 JavaScript 语法高亮器,支持 192 种语言和 498 个主题。

好了,我们开始动手来开发一个这个预览组件吧!

我们在文档的项目中 theme 文件夹下创建一个 preview 文件夹然后在里面创建一个 vue 文件,这个就是我们要准备开发的组件。我们随便给里面写点东西,我们上面说了 theme 文件夹下的 index.js 文件中 enhanceApp 方法可以通过入参中的 app 相当于当前组件库文档的 vue 实例,那么我们就可以在这个上面进行注册我们的组件.

preview 组件:

<template>
  <div><slot /></div>
</template>

<script setup></script>

<style scoped></style>
import DefaultTheme from "vitepress/theme";
import TestUI from "@test-ui/components";
import "@test-ui/theme-chalk/index.less"; // 引入样式文件
import Preview from "./preview/index.vue";

export default {
  ...DefaultTheme,
  enhanceApp: async ({ app, router, siteData }) => {
    app.use(TestUI);
    app.component("preview", Preview); // 注册预览功能的组件
  },
};

我们在markdown-examples.md中使用一下我们注册的组件,发现可以直接使用,那说明我们成功注册了,接下来就要写逻辑了。

因为我们后续有很多组件的文档,我们需要规划一下我们组件文档的结构,我们创建如下文档结构,便于我们清晰主观的书写组件文档,demo.vue 我们会写组件的使用代码,index.md 就是我们一个组件所有的使用说明。

project-20240812-5.png

index.md

# Button 按钮

<preview>
  <t-button>button</t-button>
</preview>

我们修改一下 config.mts 配置文件

import { defineConfig } from "vitepress";

export default defineConfig({
  title: "My Awesome Project",
  description: "A VitePress Site",
  themeConfig: {
    nav: [
      { text: "首页", link: "/" },
      { text: "组件", link: "/components/button/" },
    ],

    sidebar: {
      "/components/": [
        {
          text: "基本",
          items: [{ text: "Button 按钮", link: "/components/button/" }],
        },
      ],
    },
    socialLinks: [{ icon: "github", link: "https://github.com/vuejs/vitepress" }],
  },
});

修改完成之后我们是这个样子

project-20240812-6.png

我们安装一下 highlight.js

pnpm i highlight.js @highlightjs/vue-plugin -w

然后使用

import DefaultTheme from "vitepress/theme";
import TestUI from "@test-ui/components";
import "@test-ui/theme-chalk/index.less"; // 引入样式文件
import Preview from "./preview/index.vue";
import "highlight.js/styles/base16/summerfruit-light.css"; // 主题
import hljsVuePlugin from "@highlightjs/vue-plugin";

export default {
  ...DefaultTheme,
  enhanceApp: async ({ app, router, siteData }) => {
    app.use(TestUI);
    app.component("preview", Preview); // 注册预览功能的组件
    app.use(hljsVuePlugin);
  },
};

preview 组件的封装如下代码,相信大家都能看懂,就不做解释了

<template>
  <div class="demo_preview">
    <div class="preview_box">
      <slot />
    </div>
    <div class="code_box">
      <div class="code" :class="{ show_code: showCode }">
        <div class="code__reference">
          <div class="code_content">
            <highlightjs autodetect :code="sourceCode" />
          </div>
        </div>
      </div>
      <div class="operate_btn" @click="handleClick">{{ showCode ? "隐藏" : "显示" }}代码</div>
    </div>
  </div>
</template>

<script setup>
import { onMounted, reactive, toRefs } from "vue";
import "highlight.js";

const props = defineProps({
  compName: {
    type: String,
    default: "",
  },
  demoName: {
    type: String,
    default: "",
  },
});

onMounted(() => {
  componentCode();
});

const state = reactive({
  sourceCode: "",
  showCode: false,
  demoHTML: "",
});

const { sourceCode, showCode, demoHTML } = toRefs(state);

const componentCode = async () => {
  const data = await import(`../../../components/${props.compName}/${props.demoName}.vue?raw`);
  state.sourceCode = data.default;
};

const handleClick = () => {
  state.showCode = !state.showCode;
};
</script>

<style scoped>
.demo_preview {
  margin: 20px 0;
  border: 1px solid #efefef;
  border-radius: 6px;
  overflow: hidden;
}
.preview_box {
  padding: 20px;
}
.operate_btn {
  position: relative;
  height: 46px;
  line-height: 46px;
  color: #666;
  text-align: center;
  background: #f7f7f7;
  cursor: pointer;
  z-index: 100;
}
.operate_btn:hover {
  background: #f2f2f2;
}
.code {
  border-top: 1px solid #efefef;
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
}
.code .code__reference {
  overflow: hidden;
}
.show_code {
  grid-template-rows: 1fr;
}
</style>

我们在 demo1.vue 的文件中写几个示例

<template>
  <t-button>aaa</t-button>
  <t-button>bbb</t-button>
</template>

我们重新修改一下 button 的 md 文档

<script setup>
import demo1 from './demo1.vue'
</script>

# Button 按钮

<preview comp-name="button" demo-name="demo1">
  <demo1/>
</preview>

我们来看一下效果:

project-20240812-7.png

到此,我们的组件库文档大体已经完成了,剩下的就是大家自己根据喜好去配置你的首页、菜单之类的,下节我们将开始封装组件,还希望大家能够持续关注,多多点赞 🤝