vuePress 实现公用组件、业务组件在线文档化

1,415 阅读3分钟

前言

公司项目随着开发时间的推移,公用的组件跟业务组件越来越多,每次有新的功能开发或者是新的同事,开发时总是要看之前的代码才知道组件的使用与效果是否适合直接拿来使用,就想着能否做一个像elementUI一样的在线文档

调研

原本是想着自己写一个文档网站来实现,机缘巧合下发现elementUI就是使用vuePress来实现的,非常方便的可以使用markdown来实现,果断选择~

image.png

项目搭建阶段

根据官网的快速上手教程,目录是相对很简单的,可以通过控制台npm run docs:dev 来查看项目是否正常运行

新增业务文件夹

vuePress是可以将指定目录的markdown文件渲染成html文件,由于我希望在一个项目中维护PC跟移动端两个项目的组件,所以我们新建不同的文件夹来进行存放,README.md是会被默认读取的,相当于index.html

image.png

安装插件

上述说过了,vuePress强大的地方就在于可以直接支持在md文件中使用vue的语法,换言之就是我们将项目中用到的插件在项目也安装一下,后面只需要将写好的组件,全部内容拷贝即可,非常方便,在pageage.json安装项目中用到的插件,下面是我项目中的插件,仅供参考

{
  "name": "onlinedoc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "docs:dev": "vuepress dev docs",
    "docs:build": "vuepress build docs"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ali-oss": "^6.16.0",
    "ant-design-vue": "^1.7.8",
    "async-validator": "^1.11.5",
    "awe-dnd": "^0.3.4",
    "axios": "^0.21.4",
    "core-js": "^2.6.9", // 这里是解决ant框架在run build的时候,报错的问题
    "css-loader": "^5.2.5",
    "echarts": "^5.2.1",
    "express": "^4.17.1",
    "moment": "^2.29.1",
    "node-sass": "^4.14.1",
    "sass-loader": "^8.0.0",
    "sortablejs": "^1.13.0",
    "store": "^2.0.12",
    "v-viewer": "^1.5.1",
    "style-resources-loader": "^1.4.1",
    "vue-infinite-scroll": "^2.0.2",
    "vuedraggable": "^2.24.3",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "less": "^3.0.4",
    "less-loader": "^5.0.0",
    "style-loader": "^2.0.0",
    "vuepress": "^1.8.2",
    "vuepress-plugin-demo-container": "^0.2.0",
    "vuepress-plugin-demo-container-v2": "^2.3.4" // 这个插件是让md支持vue的写法,并且解析成html效果
  }
}

项目全局配置

我们在这个目录中,/docs/.vupress/ 增加配置文件,新建名为config.js的文件,文件内的写法跟webpack的配置文件方式相同,内容就是全局的一些配置与插件的使用

image.png

const path = require("path");
const fs = require('fs')
const ENV = process.env.npm_lifecycle_event; //获取当前运行的脚本指令,例如 npm run build 则当前变量为'build'
function getDocPath(basePath) {
    // 根据路径获取markdown文件
    let fileArr = [basePath]
    fs.readdirSync(path.resolve(`docs${basePath}`)).map((t) => {
        if (t !== 'README.md') {
            // REALMD.md 放到数组中会导致文档解析出错
            fileArr.push(`${basePath}${t}`)
        }
    })
    return fileArr
}

module.exports = {
    title: '奇享科技APP组件文档',  // 设置网站标题
    description: '组件文档', // 描述
    base: ENV == 'docs:dev' ? '/components/api/' :'/dist/', // 开发环境跟部署时的基础路径不同,此处需要注意的是,run docs:build的情况下,这个会影响html引用的静态资源,这里写/dist/的前提是因为我服务器的根目录指向的是.vuePress这个目录
    themeConfig: {
        nav: [
            {text: '业务平台WEB', link: '/WEB/'}, // 配置的自定义导航栏
            {text: '奇享APP', link: '/APP/'},
        ],
        sidebar: { // 配置侧边栏部分
            '/WEB/': [
                {
                    title: '开发前必读',   // 必要的
                    path: '/WEB/develop/',      // 可选的, 标题的跳转链接,应为绝对路径且必须存在
                    sidebarDepth: 1,    // 可选的, 默认值是 1
                    children: getDocPath('/WEB/develop/')
                },
                {
                    title: '公用组件',   // 必要的
                    path: '/WEB/public/',      // 可选的, 标题的跳转链接,应为绝对路径且必须存在
                    sidebarDepth: 1,    // 可选的, 默认值是 1
                    children: getDocPath('/WEB/public/')
                },
                {
                    title: '业务组件',   // 必要的
                    path: '/WEB/business',      // 可选的, 标题的跳转链接,应为绝对路径且必须存在
                    children: getDocPath('/WEB/business/')
                },
            ],
            '/APP/': [
                {
                    title: '公用组件',   // 必要的
                    path: '/APP/public/',      // 可选的, 标题的跳转链接,应为绝对路径且必须存在
                    sidebarDepth: 1,    // 可选的, 默认值是 1
                    children: getDocPath('/APP/public/')
                },
                {
                    title: '业务组件',   // 必要的
                    path: '/APP/business',      // 可选的, 标题的跳转链接,应为绝对路径且必须存在
                    children: getDocPath('/APP/business/')
                },
            ],
        },
        sidebarDepth: 2
    },
    plugins: ['demo-container-v2'], // 让md文件可以直接支持vue组件的写法
    configureWebpack: {
        resolve: {
            alias: {
                '@': path.resolve(__dirname, './'), // 设置路径别名
            }
        }
    }
}

enhanceApp.js 全局项目入口文件

enhanceApp.js相当于vue项目中的main.js,在这里引用全局的一些UI框架跟挂载原型链的一些属性这里特别注意,用到阿里OSS的图片上传的插件的组件不能使用,或者run docs:build下会报错

image.png

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

import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
import './assets/css/base.css'
import './assets/css/element-variables.scss'
import './assets/css/iconfont.css'
import './assets/css/xdeas-ui.less'
import './assets/css/override-ant.less'
import './assets/fonts/iconfont.css';
import Viewer from 'v-viewer'
import 'viewerjs/dist/viewer.css'
import * as echarts from 'echarts';
// import infiniteScroll from 'vue-infinite-scroll'
// Vue.config.productionTip = false;
import components from './common/index';
export default async ({
  Vue
}) => {
  if (typeof process === 'undefined') {
    Vue.prototype.$echarts = echarts
    Vue.use(Antd)
    Vue.use(components);
    Vue.use(Viewer);
    // Vue.use(infiniteScroll);
  }
}

组件文档编写

在WEB或者APP下新建一个md文件,这里拿选择时间的组件举例,新建一个select-time.md

image.png

在md文件第一行# 后面写的内容就侧边栏的组件的名称,建议中英文都写,因为目前中文不支持搜索,关键在于:::demo :::的写法,里面嵌套的内容可以被识别,并且呈现效果,而在vue 里面的内容就能支持vue的写法,换言之就是把vue组件中的写法拷贝放在vue 中即可.其他的描述文档根据自己的情况来写即可

# 时间范围选择 - select-time

#### 该组件用于选择日或者月的范围选择

:::warning 该组件需要引用后使用,并没有挂载为全局组件
:::

### 最基础的用法

:::demo

```vue
<template>
    
        <div class="scroll-y hiddenScroll" style="height:400px;position: relative;overflow-y: auto" >
            <select-time  :option="timeObj" @handlePanelChange="timeChange"></select-time>
        </div>
</template>
<script>
import area_name from "@/components/area_name"

export default {
    components: {
        area_name
    },
    data() {
        return {
            timeObj:{
                type: "dayTime",
                rangetime: '',
            },
        }
    },
    methods: {
        timeChange(){
            
        },
    }
}
</script>
```

:::

### API

:::demo

```html

<template>
    <api_table :data="tableData"></api_table>
</template>
<script>
    export default {
        data() {
            return {
                tableData: [
                    {
                        name: "option",
                        description: "type:dayTime--日范围选择/ monthTimeRange--月范围选择,   rangeTime:[默认的开始时间,默认的结束时间]",
                        type: "Object",
                        default: '{type:"dayTime",rangetime:[]}'
                    },

                    {
                        name: "maxLengthToMonth",
                        description: "当月份范围选择时,最多能选择多少个月",
                        type: "Number",
                        default: '36'
                    },
                    {
                        name: "maxLengthToDay",
                        description: "当日范围选择时,最多能选择多少天",
                        type: "Number",
                        default: '90'
                    },
                    {
                        name: "clearable",
                        description: "是否展示清除按钮,目前只有在月份范围选择中起效",
                        type: "Boolean",
                        default: 'false' 
                    },
                    {
                        name: "future",
                        description: "是否允许选择今天之后的时间",
                        type: "Boolean",
                        default: 'false'
                    }
                ],
            }
        },
        methods: {
            getList() {
            },
        }
    }
</script>
<style lang="less">
    .basic-table-box {
        top: 0;
    }
</style>
```
:::


### 事件

:::demo

```html

<template>
    <fn_table :data="tableData"></fn_table>
</template>
<script>
    export default {
        data() {
            return {
                tableData: [
                    {
                        name: "handlePanelChange",
                        description: "选择了时间的回调,参数是数组,第一个元素为开始时间,第二个元素为结束时间",
                        callback: "([开始时间,结束时间])=>{ }"
                    },
                ]
            }
        },
    }
</script>
```

:::

最终效果~~~代码如下

完整代码DEMO

公式.gif