初始化项目
使用vue-cli3初始化项目,初始化目录如下:
将src修改为packages,用于放置组件源文件。新建example目录用于放组件案例,获得最新目录:
配置vue.config.js
修改目录后需要修改打包文件中对应的文件名,在vue-cli3中需要新增vue.config.js
来扩展打包配置,参考文档: https://cli.vuejs.org/zh/config/
配置如下:
module.exports = {
outputDir: 'dist', // 输出目录(打包后的文件夹)
publicPath: './',
pages: {
index: {
entry: 'example/main.js', // 入口文件(开发和生产中案例的入口文件)
template: 'public/index.html',
filename: 'index.html'
}
}
}
按照以上配置执行
npm run build
后会直接生成dist目录,且将example中的案例打包,不是组件文件的打包
配置package.json
修改package.json
文件:
在script
中新增lib
命令"lib": "vue-cli-service build --target lib --name base-main --dest lib packages/index.js"
--target: app | lib | wc | wc-async (默认值:app)
--name: 打包后的组件名称
--dest: 指定输出目录 (默认值:dist)
packages/index.js: 入口js文件
参考文档:https://cli.vuejs.org/zh/guide/cli-service.html#vue-cli-service-build
运行npm run lib
,生成lib文件夹和组件文件base-main.umd.min.js
上传组件文件到npm
- 配置 package.json 文件中发布到 npm 的字段
name: 包名,该名字是唯一的。可在 npm 官网搜索名字,如果存在则需换个名字。
version: 版本号,每次发布至 npm 需要修改版本号,不能和历史版本号相同。
description: 描述。
main: 入口文件,该字段需指向我们最终编译后的组件包文件(上面的lib文件夹中的lib/base-main.umd.min.js)。
keyword:关键字,以空格分离希望用户最终搜索的词。
author:作者
private:是否私有,需要修改为 false 才能发布到 npm
license: 开源协议(可以填自己的github地址) - 添加 .npmignore 文件,设置忽略发布文件
发布到 npm 中,只有编译后的 lib 目录、package.json、README.md才是需要被发布的。所以我们需要设置忽略目录和文件。和 .gitignore 的语法一样,具体需要提交什么文件,看各自的实际情况。 - 登录npm
npm login
如果配置了淘宝镜像,先设置回npm镜像:
npm config set registry http://registry.npmjs.org/
发布到npm
npm publish-
更新npm版本包
使用npm version <update_type>
,对npm版本进行更新,版本号的三位分别是大号·中号·小号·预发布号
。
update_type可以为以下值:- prerelease:有预发布号的,版本号+1;无预发布号的,小号+1且预发布号初始为0
运行:npm version prerelease
package.json 中的版本号1.0.0变为 1.0.1-0
再运行
package.json 中的版本号1.0.1-0变为 1.0.1-1
- prepatch:小号+1;预发布号初始为0
运行:npm version prepatch
1. package.json 中的版本号1.0.0变为 1.0.1-0
2. package.json 中的版本号1.0.1-1变为 1.0.2-0
- preminor:中号+1;小号和预发布号初始为0
运行:npm version preminor
1. package.json 中的版本号1.0.2-0变为 1.1.0-0
2. package.json 中的版本号1.0.1-1变为 1.1.0-0
- premajor:大号+1;中号,小号和预发布号初始为0
运行:npm version premajor
1. package.json 中的版本号1.1.0-0变为 2.0.0-0
- patch:有预发布号的去掉预发布号,其他不变;无预发布号的小号+1
运行:npm version patch
1. package.json 中的版本号1.1.0-0变为 1.1.0
2. package.json 中的版本号1.1.0变为 1.1.1
- minor:有预发布号的,小号为0时去掉预发布号,其他不变,小号不为0时中号+1且其他置为0去掉预发布号;无预发布号的中号+1,小号置为0
运行:npm version minor
1. package.json 中的版本号1.1.0变为 1.2.0
2. package.json 中的版本号1.1.0-0变为 1.1.0
3. package.json 中的版本号1.1.1-0变为 1.2.0
- major: 无预发布号的,大号+1其他置为0;有预发布号的,中号和小号为0时去除预发布号,其他不变。如果中号和小号中有一个不为0的话,大号+1,其他重置为0,去除预发布号
运行:npm version major
1. package.json 中的版本号1.1.0变为 2.0.0
2. package.json 中的版本号1.0.0-0变为 1.0.0
3. package.json 中的版本号1.1.1-0变为 2.0.0
UI文档的编写
这一块参考的element-ui
做法,需要新增外部包:
highlight.js // 用于代码的高亮
transliteration // 用于中文拼音转换
markdown-it
markdown-it-anchor
markdown-it-container
vue-markdown-loader
新增demo-block.vue文件
用于展示案例效果和代码,参考element-ui
的demo-block.vue文件做了修改,去除了其他语言,如下:
<template>
<div
class="demo-block"
:class="[blockClass, { 'hover': hovering }]"
@mouseenter="hovering = true"
@mouseleave="hovering = false">
<div class="source">
<slot name="source"></slot>
</div>
<div class="meta" ref="meta">
<div class="description" v-if="$slots.default">
<slot></slot>
</div>
<div class="highlight">
<slot name="highlight"></slot>
</div>
</div>
<div
class="demo-block-control"
ref="control"
:class="{ 'is-fixed': fixedControl }"
@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: .2s;
&.hover {
box-shadow: 0 0 8px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .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 .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;
&::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: .3s;
&.hovering {
transform: translateX(-40px);
}
}
> span {
position: absolute;
transform: translateX(-30px);
font-size: 14px;
line-height: 44px;
transition: .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 {
data() {
return {
hovering: false,
isExpanded: false,
fixedControl: false,
scrollParent: null,
langConfig: {
"hide-text": "隐藏代码",
"show-text": "显示代码"
}
};
},
props: {
jsfiddle: Object,
default() {
return {};
}
},
methods: {
scrollHandler() {
const { top, bottom, left, width } = this.$refs.meta.getBoundingClientRect();
this.fixedControl = bottom > document.documentElement.clientHeight &&
top + 44 <= document.documentElement.clientHeight;
this.$refs.control.style.left = this.fixedControl ? `${ left }px` : '0';
this.$refs.control.style.width = this.fixedControl ? `${ width }px` : 'auto';
},
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('#ex-r-area');
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>
配置vue.config.js
修改vue.config.js文件,新增chainWebpack属性,用于文档文件的转换。如下:
chainWebpack: config => {
// 设置文件夹别名
config.resolve.alias
.set('@', resolve('example'))
.set('~', resolve('packages'))
config.module
.rule('js')
.include
.add(__dirname + 'packages')
.end()
.use('babel')
.loader('babel-loader')
.tap(options => {
// 修改它的选项...
return options
})
config.module
.rule('md')
.test(/\.md/)
.use('vue-loader')
.loader('vue-loader')
.end()
.use('vue-markdown-loader')
.loader('vue-markdown-loader/lib/markdown-compiler')
.options({
raw: true,
preventExtract: true, //这个加载器将自动从html令牌内容中提取脚本和样式标签
// 定义处理规则
preprocess: (MarkdownIt, source) => {
// 对于markdown中的table,
MarkdownIt.renderer.rules.table_open = function() {
return '<table class="doctable">';
};
// 对于代码块去除v - pre, 添加高亮样式;
const defaultRender = md.renderer.rules.fence;
MarkdownIt.renderer.rules.fence = (
tokens,
idx,
options,
env,
self
) => {
const token = tokens[idx];
// 判断该 fence 是否在 :::demo 内
const prevToken = tokens[idx - 1];
const isInDemoContainer =
prevToken &&
prevToken.nesting === 1 &&
prevToken.info.trim().match(/^demo\s*(.*)$/);
if (token.info === "html" && isInDemoContainer) {
return `<template slot="highlight"><pre v-pre><code class="html">${md.utils.escapeHtml(
token.content
)}</code></pre></template>`;
}
return defaultRender(tokens, idx, options, env, self);
};
return source;
},
use: [
// 标题锚点
[
require("markdown-it-anchor"),
{
level: 2, // 添加超链接锚点的最小标题级别, 如: #标题 不会添加锚点
slugify: slugify, // 自定义slugify, 我们使用的是将中文转为汉语拼音,最终生成为标题id属性
permalink: true, // 开启标题锚点功能
permalinkBefore: true // 在标题前创建锚点
}
],
// :::demo ****
//
// :::
//匹配:::后面的内容 nesting == 1,说明:::demo 后面有内容
//m为数组,m[1]表示 ****
[
require("markdown-it-container"),
"demo",
{
validate: function(params) {
return params.trim().match(/^demo\s*(.*)$/);
},
render: function(tokens, idx) {
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
if (tokens[idx].nesting === 1) {
//
const description = m && m.length > 1 ? m[1] : ""; // 获取正则捕获组中的描述内容,即::: demo xxx中的xxx
const content =
tokens[idx + 1].type === "fence"
? tokens[idx + 1].content
: "";
return `<demo-block>
<div slot="source">${content}</div>
${description ? `<div>${md.render(description)}</div>` : ""}
`;
}
return "</demo-block>";
}
}
],
[require("markdown-it-container"), "tip"],
[require("markdown-it-container"), "warning"]
]
});
}
设置文档文件
在example中新增doc文件夹用于存放md文件,在md文件中使用markdown语法书写文档。其中以:::demo
开始对组件进行使用的讲解,在demo后面可以填写组件的中相关的属性和方法的使用,在```html 代码块 ```
的代码块中填写组件案例的使用代码,和函数方法。案例:
:::demo 使用`type`、`plain`、`round`和`circle`属性来定义 Button 的样式。
```html
<div class="mb-20">
<el-button>默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="success">成功按钮</el-button>
<el-button type="info">信息按钮</el-button>
<el-button type="warning">警告按钮</el-button>
<el-button type="danger">危险按钮</el-button>
</div>
<script lang="babel">
export default{
}
</script>
``` // html的结尾符
:::
修改example中的路由文件,设置对应案例的对应的路由,如下:
new Router({
routes: [
{
path: '/ElButton',
name: 'ElButton',
text: 'button按钮',
component: () => import(`@/doc/ElButton.md`)
}
]
})
预览地址:https://erpang123.github.io/C-UI/CUI/index.html
参考的相关文章:https://blog.csdn.net/qq_31126175/article/details/100527322
https://segmentfault.com/a/1190000021140844