组件库系列三:编写组件库文档

vuepress介绍

  • Vue 驱动的静态网站生成器
  • 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
  • 享受 Vue + webpack 的开发体验,可以在 Markdown 中使用 Vue 组件,又可以使用 Vue 来开发自定义主题。
  • VuePress 会为每个页面预渲染生成静态的 HTML,同时,每个页面被加载的时候,将作为 SPA 运行。

创建文档工程

另起一个目录,创建por-ui-doc文件夹

yarn init -y  // 创建package.json
yarn add vuepress -D // 安装vuepress框架

配置运行指令

在package.json中

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

vuepress浏览器自动更新

本地开发模式运行Vuepress 1.x 时,浏览器不能自动更新

`"dev": "vuepress dev docs"`

改为

`"dev": "vuepress dev docs --temp .temp"`

下载插件和依赖

element-ui、highlight.js(代码高亮)、sass

element-ui、highlight.js 可以直接下载

yarn add element-ui highlight.js -D

node-sass和sass-loader直接下载会有版本过高问题,版本匹配:5.0.0配合10.1.1

yarn add sass-loader@10.1.1 -D
yarn add node-sass@5.0.0 -D

npm/yarn link

由于组件库项目和文档项目之间存在依赖,可以使用yarn link将一个项目链接到另一个项目。

项目A中需要使用项目B时,可以使用yarn link或npm link将B引入到A。

PS:link本身是软链接,yarn link是将资源存在yarn的内存中,相当于建立了一个通道。

# 进入B项目,创建链接对象
yarn link
# 进入A项目,建立B项目的链接
yarn link B
# 解除链接
yarn unlink B

注意:

  1. A和B均为package.json文件中的“name”对应的值,一般为文件夹名称

  2. 项目B是我们自己开发封装的组件,引入前需要先完成打包

  3. 使用完成后使用unlink解除链接

docs文件夹

在根目录下创建docs文件夹

在该文件夹下,创建README.md文件,两个的名称都不能改

home: true // 是否首页
actionText: 欢迎 → // 首页文本
actionLink: /components/button
features:
- title: por-ui官方文档
  details: por-ui官方文档

在该文件夹下,创建components文件夹

里面放每个组件都有一个md文件,例如button.md

注意文件中需要有标题,不然显示的是链接形式

# 按钮组件

或者设置title

---
title: Button
---

查看效果

运行yarn run dev

.vuepress文件夹

  1. 创建.vuepress文件夹

在docs文件夹下创建.vuepress文件夹,在里面创建config.js,用来配置导航等文档基本结构信息,修改后重启项目才生效

module.exports = {
  title: 'por-ui', // 设置网站标题
  description: 'ui 库', //描述
  dest: './build', // 设置输出目录
  port: 1234, //端口
  markdown: {
    anchor: { permalink: false },
  },
  themeConfig: { //主题配置
    nav: [{
        text: '主页',
        link: '/'
      }, // 导航条
    ],
    // 为以下路由添加侧边栏
    sidebar: [{
        title: '介绍',
        collapsable: true, // 可折叠
        children: ["/introduce/"]
      },
      {
        title: '组件',
        collapsable: false,
        children: [
        	// 按钮组件
          "/components/button"
        ]
      }
    ]
  }
}
  1. 去掉默认样式

在.vuepress文件夹下创建styles文件夹,在里面创建palette.styl文件写自己的样式去格式化掉框架自带的一些默认样式

$codeBgColor = #fafafa // 代码背景颜色

$accentColor = #3eaf7c
$textColor = #2c3e50

$borderColor = #eaecef
$arrowBgColor = #ccc
$badgeTipColor = #42b983
$badgeWarningColor = darken(#ffe564, 35%)
$badgeErrorColor = #DA5961

.content pre{  margin: 0!important;}

.theme-default-content:not(.custom){
    max-width: 1000px !important;
}
  1. enhanceApp.js入口文件

在页面里要用上自己的组件,要在.vuepress文件夹下创建一个enhanceApp.js文件作为入口(启动当前的vuepress,这个文件就是当前项目的一个入口),框架规定的,名字不能改

import Vue from 'vue';
import Element from 'element-ui'; // 引入elementUi,因为要用到elementui里的结构
import 'element-ui/lib/theme-chalk/index.css'

import hljs from 'highlight.js' //引入高亮js
import 'highlight.js/styles/googlecode.css' //引入高亮js样式文件

//正常情况引入porui要根据目录../../一层一层去引入的,
//这里为了方便在porui文件夹根目录下npm link一下,把porui添加到本地全局,
//注意要以package.json里的name名字为准
//然后在porui-doc文件夹下npm link por-ui一下,把本地全局的porui引入到文档项目下
//成功之后在porui-doc下的nodemodule下就能看到这个porui文件夹了(实际上是个链接,每次porui文件夹改动了,这里的也会改动)
//这样就可以以下面一行的方式引用了
import porUi from 'por-ui' // 要编写对应的文档的包
import 'por-ui/dist/por-ui.css'
// 写了一个高亮指令,可以去解析pre code里面的标签,添加高亮
Vue.directive('highlight', function(el) {
  let blocks = el.querySelectorAll('pre code');
  blocks.forEach((block) => {
    hljs.highlightBlock(block)
  })
})
export default ({
  Vue,
  options,
  router,
  siteData
}) => {
  Vue.use(Element);
  Vue.use(porUi) // 设置为全局组件
}
  1. components文件夹

    在.vuepress文件夹下,创建components文件夹,放置为引入的poly-ui写一些测试代码组件,只要是这个目录下的都是全局组件,例如:在里面创建button文件夹,里面放button1.vue,那么buton1.vue就是button的测试

    // button1.vue
    <template>
      <div>
        <por-button>默认按钮</por-button>
        <por-button type='primary'>主要按钮</por-button>
        <por-button type='warning'>警告按钮</por-button>
        <por-button type='danger'>危险按钮</por-button>
        <por-button type='success'>成功按钮</por-button>
        <por-button type='info'>信息按钮</por-button>
      </div>
    </template>
    <script>
    export default {}
    </script>
    

    然后button1.vue就可以在docs目录下的components内的所有.md文件内使用了

    #按钮组件
    //button是.vuepress内components下文件夹button的名字,-后面的button1是button文件夹下button1.vue的名字
    //找到的就是button1.vue,它会把这个文件渲染到文档上
    <button-button1></button-button1>。
    
  2. core-js报错

如果遇到core-js报错,就yarn add core-js@2重新装一下

可收缩代码块

创建可收缩代码块

在.vuepress内components下文件夹下创建demo-block可收缩代码块,开发像elementui那种查看例子时,可以展开代码的功能

<template>
  <!-- 主要用到了三个插槽 -->
  <div
    class="demo-block"
    :class="[blockClass, { 'hover': hovering }]"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false"
  >
    <div style="padding:24px">
      <!-- 第一个是源代码插槽 -->
      <slot name="source"></slot>
    </div>
    <div class="meta" ref="meta">
      <div class="description" v-if="$slots.default">
        <!-- 第二个是描述插槽 -->
        <slot></slot>
      </div>
      <div class="highlight" v-highlight>
        <!-- 第三个是高亮插槽 -->
        <slot name="highlight"></slot>
      </div>
    </div>
    <div class="demo-block-control" ref="control" @click="isExpanded = !isExpanded">
      <transition name="arrow-slide">
        <i :class="[iconClass, { 'hovering': hovering }]"></i>
      </transition>
      <transition name="text-slide">
        <span v-show="hovering">{{ controlText }}</span>
      </transition>
    </div>
  </div>
</template>

<style lang="scss">
.demo-block {
  border: solid 1px #ebebeb;
  border-radius: 3px;
  transition: 0.2s;
  &.hover {
    box-shadow: 0 0 8px 0 rgba(232, 237, 250, 0.6),
      0 2px 4px 0 rgba(232, 237, 250, 0.5);
  }

  code {
    font-family: Menlo, Monaco, Consolas, Courier, monospace;
  }

  .demo-button {
    float: right;
  }

  .source {
    padding: 24px;
  }

  .meta {
    background-color: #fafafa;
    border-top: solid 1px #eaeefb;
    overflow: hidden;
    height: 0;
    transition: height 0.2s;
  }

  .description {
    padding: 20px;
    box-sizing: border-box;
    border: solid 1px #ebebeb;
    border-radius: 3px;
    font-size: 14px;
    line-height: 22px;
    color: #666;
    word-break: break-word;
    margin: 10px;
    background-color: #fff;

    p {
      margin: 0;
      line-height: 26px;
    }

    code {
      color: #5e6d82;
      background-color: #e6effb;
      margin: 0 4px;
      display: inline-block;
      padding: 1px 5px;
      font-size: 12px;
      border-radius: 3px;
      height: 18px;
      line-height: 18px;
    }
  }

  .highlight {
    pre {
      margin: 0;
    }

    code.hljs {
      margin: 0;
      border: none;
      max-height: none;
      border-radius: 0;
      line-height: 1.8;
      color: black;
      &::before {
        content: none;
      }
    }
  }

  .demo-block-control {
    border-top: solid 1px #eaeefb;
    height: 44px;
    box-sizing: border-box;
    background-color: #fff;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    text-align: center;
    margin-top: -1px;
    color: #d3dce6;
    cursor: pointer;
    position: relative;

    &.is-fixed {
      position: fixed;
      bottom: 0;
      width: 868px;
    }

    i {
      font-size: 16px;
      line-height: 44px;
      transition: 0.3s;
      &.hovering {
        transform: translateX(-40px);
      }
    }

    > span {
      position: absolute;
      transform: translateX(-30px);
      font-size: 14px;
      line-height: 44px;
      transition: 0.3s;
      display: inline-block;
    }

    &:hover {
      color: #409eff;
      background-color: #f9fafc;
    }

    & .text-slide-enter,
    & .text-slide-leave-active {
      opacity: 0;
      transform: translateX(10px);
    }

    .control-button {
      line-height: 26px;
      position: absolute;
      top: 0;
      right: 0;
      font-size: 14px;
      padding-left: 5px;
      padding-right: 25px;
    }
  }
}
</style>

<script type="text/babel">
export default {
  name: 'demo-block',
  data () {
    return {
      hovering: false,
      isExpanded: false,
      fixedControl: false,
      scrollParent: null,
      langConfig: {
        "hide-text": "隐藏代码",
        "show-text": "显示代码",
        "button-text": "在线运行",
        "tooltip-text": "前往 jsfiddle.net 运行此示例"
      }
    }
  },

  props: {
    jsfiddle: Object,
    default () {
      return {}
    }
  },

  methods: {
    scrollHandler () {
      const { top, bottom, left } = this.$refs.meta.getBoundingClientRect()
      this.fixedControl = bottom > document.documentElement.clientHeight &&
        top + 44 <= document.documentElement.clientHeight
    },

    removeScrollHandler () {
      this.scrollParent && this.scrollParent.removeEventListener('scroll', this.scrollHandler)
    }
  },

  computed: {
    lang () {
      return this.$route.path.split('/')[1]
    },

    blockClass () {
      return `demo-${this.lang} demo-${this.$router.currentRoute.path.split('/').pop()}`
    },

    iconClass () {
      return this.isExpanded ? 'el-icon-caret-top' : 'el-icon-caret-bottom'
    },

    controlText () {
      return this.isExpanded ? this.langConfig['hide-text'] : this.langConfig['show-text']
    },

    codeArea () {
      return this.$el.getElementsByClassName('meta')[0]
    },

    codeAreaHeight () {

      if (this.$el.getElementsByClassName('description').length > 0) {
        return this.$el.getElementsByClassName('description')[0].clientHeight +
          this.$el.getElementsByClassName('highlight')[0].clientHeight + 20
      }
      return this.$el.getElementsByClassName('highlight')[0].clientHeight
    }
  },

  watch: {
    isExpanded (val) {
      this.codeArea.style.height = val ? `${this.codeAreaHeight + 1}px` : '0'
      if (!val) {
        this.fixedControl = false
        this.$refs.control.style.left = '0'
        this.removeScrollHandler()
        return
      }
      setTimeout(() => {
        this.scrollParent = document.querySelector('.page-component__scroll > .el-scrollbar__wrap')
        this.scrollParent && this.scrollParent.addEventListener('scroll', this.scrollHandler)
        this.scrollHandler()
      }, 200)
    }
  },

  mounted () {
    this.$nextTick(() => {
      let highlight = this.$el.getElementsByClassName('highlight')[0]
      if (this.$el.getElementsByClassName('description').length === 0) {
        highlight.style.width = '100%'
        highlight.borderRight = 'none'
      }
    })
  },

  beforeDestroy () {
    this.removeScrollHandler()
  }
};
</script>

在组件.md文件中使用

# Button组件
常用的操作按钮。
## 基础用法
基础的按钮用法。

<demo-block>
::: slot source
<button-button1></button-button1>
:::

使用type属性来定义 Button 的样式。

::: slot highlight

```html
<div>
    <por-button>默认按钮</por-button>
    <por-button type="primary">主要按钮</por-button>
    <por-button type="success">成功按钮</por-button>
    <por-button type="info">信息按钮</por-button>
    <por-button type="warning">警告按钮</por-button>
    <por-button type="danger">危险按钮</por-button>
</div>
```
:::
</demo-block>

到这里就基本上完成了一个文档的功能

效果展示

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值