一、使用Vue Cli创建项目
1、安装Vue Cli
npm install -g @vue/cli
# OR
yarn global add @vue/cli
2、创建项目
vue create test-ui-vue
二、修改目录,以及配置文件
1、将src目录改为examples
用来展示组件。
2、创建同级别的目录packages
,用来存放自定义组件。
3、由于修改了目录,所以需要重新新配置webpack
,先在最外层创建vue.config.js
。现在的目录结构如下:
TEST-UI-VUE
├── examples # demo源码
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── router # 路由
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ └── main.js # 入口文件 加载组件 初始化等
├── packages # 自定义组件模板
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 自定义组导出
│ └── index.js # 自定义组件入口,在此处导出所有组件
├── tests # 测试
├── .browserslistrc # 浏览器和 node 版本的配置
├── .eslintrc.js # eslint 配置项.
├── .gitignore # gitignore
├── babel.config.js # babel-loader 配置
├── jest.config.js # jest 配置
├── package.json # package.json
├── README.md # README.md
└── vue.config.js # vue-cli 配置
4、配置vue.config.js文件并运行项目。
const path = require('path')
module.exports = {
// 修改 pages 入口
pages: {
index: {
entry: 'examples/main.js', // 入口
template: 'public/index.html', // 模板
filename: 'index.html' // 输出文件
}
},
// 扩展 webpack 配置
chainWebpack: config => {
// @ 默认指向 src 目录,这里要改成 examples
// 另外也可以新增一个 ~ 指向 packages
config.resolve.alias
.set('@', path.resolve('examples'))
.set('~', path.resolve('packages'))
// 把 packages 和 examples 加入编译,因为新增的文件默认是不被 webpack 处理的
config.module
.rule('js')
.include.add(/packages/)
.end()
.include.add(/examples/)
.end()
.use('babel')
.loader('babel-loader')
.tap(options => {
// 修改它的选项...
return options
})
}
}
三、新建组件
1、在packages
下面新建一个button
组件的文件夹,同时新建一个index.js
文件,用来导出所有组件。目录结构如下:
TEST-UI-VUE
├── examples # demo源码
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── router # 路由
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ └── main.js # 入口文件 加载组件 初始化等
├── packages # 自定义组件模板
│ └── button # button组件
│ │ │── src # button组件源码
│ │ │ └── button.vue # button组件模板
│ │ └── index.js # button组件入口,导出button组件
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 自定义组导出
│ └── index.js # 自定义组件入口,在此处导出所有组件
├── tests # 测试
├── .browserslistrc # 浏览器和 node 版本的配置
├── .eslintrc.js # eslint 配置项.
├── .gitignore # gitignore
├── babel.config.js # babel-loader 配置
├── jest.config.js # jest 配置
├── package.json # package.json
├── README.md # README.md
└── vue.config.js # vue-cli 配置
2、在button.vue
中编写组件
<template>
<button
class="ts-button"
:class="{ 'is-disabled': disabled }"
:disabled="disabled"
>
<span><slot>测试按钮</slot></span>
</button>
</template>
<script>
export default {
name: "TsButton", // 注意这个name是必须的
props: {
disabled: {
type: Boolean,
default: false,
},
},
};
</script>
<style lang="scss">
.ts-button {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
border: 1px solid #dcdfe6;
color: #606266;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
transition: 0.1s;
font-weight: 500;
user-select: none;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
&:focus,
&:hover {
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}
&.is-disabled,
&.is-disabled:focus,
&.is-disabled:hover {
color: #c0c4cc;
cursor: not-allowed;
background-image: none;
background-color: #fff;
border-color: #ebeef5;
}
}
</style>
3、在/packages/button/src/index.js
中暴露组件
import TsButton from './src/button';
/* istanbul ignore next */
TsButton.install = function(Vue) {
Vue.component(TsButton.name, TsButton);
};
export default TsButton;
4、最后在/src/index.js
中导出所有组件
import TsButton from "../packages/button/index.js";
// 所有组件列表
const components = [TsButton];
// 定义install方法,接收Vue作为参数
const install = function (Vue) {
// 判断是否安装,安装过就不继续往下执行
if (install.installed) return;
install.installed = true;
// 遍历注册所有组件
components.map((component) => Vue.use(component));
};
// 检测到Vue才执行,毕竟我们是基于Vue的
if (typeof window !== "undefined" && window.Vue) {
install(window.Vue);
}
export default {
install,
// 所有组件,必须具有install,才能使用Vue.use()
...components,
};
5、在项目中测试组件
在main.js
中引入组件
// 引入组件
import TestUi from '../src/index'
Vue.use(TestUi)
四、发布组件进行测试
1、在 package.json
的 scripts
字段中新增一下命令:
"lib": "vue-cli-service build --target lib --name test-ui-vue --dest lib src/index.js"
--target:
构建目标,默认为应用模式。这里修改为 lib
启用库模式。
--dest :
输出目录,默认 dist
。这里我们改成 lib
[entry]:
最后一个参数为入口文件,这里我们指定编译 /src/index.js/
组件库入口文件。
2、编译组件库
执行命令 npm run lib
,会发现目录下多了lib文件夹
package.json其他配置,配置如下
{
"name": "test-ui-vue",
"version": "0.1.2",
"description": "从零开始做一个基于Vue Cli的组件库",
"main": "lib/test-ui-vue.umd.min.js",
"author": "xuyong",
"license": "MIT",
"keywords": [
"test-ui"
],
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lib": "vue-cli-service build --target lib --name test-ui-vue --dest lib src/index.js",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^2.6.11",
"vue-router": "^3.2.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-unit-jest": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/test-utils": "^1.0.3",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^6.2.2",
"lint-staged": "^9.5.0",
"prettier": "^2.2.1",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"vue-template-compiler": "^2.6.11"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,vue}": [
"vue-cli-service lint",
"git add"
]
}
}
3、添加.npmignore
文件,发布时,只有编译后的 lib
目录、package.json
、README.md
才需要被发布。所以通过配置.npmignore
文件忽略不需要提交的目录和文件。
# 这是复制 .gitignore 里面的
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
# 以下是新增的
# 要忽略目录和指定文件
examples/
packages/
public/
vue.config.js
babel.config.js
*.map
*.html
4、发布到npm
现需要去npm
官网注册账号
然后本地登录:npm login
最后发布到npm: npm publish
5、最后就可以测试了:
另起一个项目
npm i test-ui-vue
在main.js
中引用
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
// 引入组件
import TestUiVue from 'test-ui-vue'
import 'test-ui-vue/lib/test-ui-vue.css'
Vue.use(TestUiVue)
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
在app.vue
中调用组件:
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
<ts-button>测试按钮</ts-button>
</div>
</template>
五、添加markdown
插件,使项目支持使用markdown
直接编写文档并显示到页面
1、安装markdown
插件
npm i vue-markdown-loader -D
2、修改vue.config.js
, 添加vue-markdown-loader
,使用webpack
能识别md
文件
const path = require("path");
module.exports = {
// 修改 pages 入口
pages: {
index: {
entry: "examples/main.js", // 入口
template: "public/index.html", // 模板
filename: "index.html", // 输出文件
},
},
// 扩展 webpack 配置
chainWebpack: (config) => {
// @ 默认指向 src 目录,这里要改成 examples
// 另外也可以新增一个 ~ 指向 packages
config.resolve.alias
.set("@", path.resolve("examples"))
.set("~", path.resolve("packages"));
// 把 packages 和 examples 加入编译,因为新增的文件默认是不被 webpack 处理的
config.module
.rule("js")
.include.add(/packages/)
.end()
.include.add(/examples/)
.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,
});
},
};
3、创建存放文档的目录及md
文件
在 examples
目录下创建 docs
文件夹,在docs
文件夹下创建 test.md,文件内容如下
## tip
:::tip
这是一个 tip。这是一个 tip。这是一个 tip。这是一个 tip。这是一个 tip。这是一个 tip。
:::
## warning
:::warning
这是一个 warning,**这是一个 warning,**。
这是一个 warning。
这是一个 warning,这是一个 warning,这是一个 warning,这是一个 warning。
:::
## demo
:::demo 这是一个 demo。这是一个 demo。这是一个 demo。这是一个 demo。这是一个 demo。这是一个 demo。这是一个 demo。这是一个 demo。
```html
<sq-button></sq-button>
```
:::
将 test.md
添加进路由进行测试
在router/index.js
中添加
{
path: '/test',
name: 'test',
component: () => import(/* webpackChunkName: "about" */ '../docs/test.md')
}
4、安装其他的markdown
插件
cnpm i markdown-it markdown-it-container -S
再次修改vue.config.js文件
const path = require("path");
// 引入markdown-it
const md = require("markdown-it")();
/**
* 增加 hljs 的 classname
*/
function wrapCustomClass(render) {
return function (...args) {
return render(...args)
.replace('<code class="', '<code class="hljs ')
.replace('<code>', '<code class="hljs">')
}
}
module.exports = {
// 修改 pages 入口
pages: {
index: {
entry: "examples/main.js", // 入口
template: "public/index.html", // 模板
filename: "index.html", // 输出文件
},
},
// 扩展 webpack 配置
chainWebpack: (config) => {
// @ 默认指向 src 目录,这里要改成 examples
// 另外也可以新增一个 ~ 指向 packages
config.resolve.alias
.set("@", path.resolve("examples"))
.set("~", path.resolve("packages"));
// 把 packages 和 examples 加入编译,因为新增的文件默认是不被 webpack 处理的
config.module
.rule("js")
.include.add(/packages/)
.end()
.include.add(/examples/)
.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) => {
MarkdownIt.renderer.rules.table_open = function () {
return '<table class="table">';
};
MarkdownIt.renderer.rules.fence = wrapCustomClass(MarkdownIt.renderer.rules.fence)
// ```code`` 给这种样式加个class code_inline
const code_inline = MarkdownIt.renderer.rules.code_inline;
MarkdownIt.renderer.rules.code_inline = function (...args) {
args[0][args[1]].attrJoin("class", "code_inline");
return code_inline(...args);
};
return source;
},
use: [
// :::demo ****
[
require("markdown-it-container"),
"demo",
{
validate: function (params) {
return params.trim().match(/^demo\s*(.*)$/);
},
render: function(tokens, idx) {
if (tokens[idx].nesting === 1) {
return `<demo-block>
<div slot="highlight">`;
}
return '</div></demo-block>\n';
}
},
],
[require("markdown-it-container"), "tip"],
[require("markdown-it-container"), "warning"],
],
});
},
};
在 examples/components 下添加 DemoBlock.vue,内容如下:
<template>
<div class="docs-demo-wrapper">
<div :style="{height: isExpand ? 'auto' : '0'}" class="demo-container">
<div span="14">
<div class="docs-demo docs-demo--expand">
<div class="highlight-wrapper">
<slot name="highlight"></slot>
</div>
</div>
</div>
</div>
<span class="docs-trans docs-demo__triangle" @click="toggle">{{isExpand ? '隐藏代码' : '显示代码'}}</span>
</div>
</template>
<script>
export default {
data() {
return {
isExpand: false
};
},
methods: {
toggle() {
this.isExpand = !this.isExpand;
}
}
};
</script>
<style lang="scss">
.demo-container {
transition: max-height .3s ease;
overflow: hidden;
}
.docs-demo {
width: 100%;
height: auto;
box-sizing: border-box;
font-size: 14px;
background-color: #F7F7F7;
border: 1px solid #e2ecf4;
border-top: none;
pre code {
font-family: Consolas,Menlo,Courier,monospace;
line-height: 22px;
border: none;
}
}
.docs-trans {
width: 100%;
text-align: center;
display: inline-block;
color: #C5D9E8;
font-size: 12px;
padding: 10px 0;
background-color: #FAFBFC;
}
.docs-demo__code,
.highlight-wrapper,
.docs-demo__meta {
padding: 0 20px;
overflow-y: auto;
}
.docs-demo__code {
border-bottom: 1px solid #eee;
}
.docs-demo.docs-demo--expand .docs-demo__meta {
border-bottom: 1px dashed #e9e9e9;
}
.docs-demo.docs-demo--expand .docs-demo__triangle {
transform: rotate(180deg);
}
.highlight-wrapper {
display: none;
p,
pre {
margin: 0;
}
.hljs {
padding: 0;
}
}
.docs-demo.docs-demo--expand .highlight-wrapper {
display: block;
}
.docs-demo__code__mobi {
height: 620px;
margin: 20px 0;
}
.docs-demo__code__mobi__header {
border-radius: 4px 4px 0 0;
background: -webkit-linear-gradient(rgba(55,55,55,.98),#545456);
background: linear-gradient(rgba(55,55,55,.98),#545456);
text-align: center;
padding: 8px;
img {
width: 100%;
}
.url-box {
height: 28px;
line-height: 28px;
color: #fff;
padding: 0 3px;
background-color: #a2a2a2;
margin: 10px auto 0;
border-radius: 4px;
white-space: nowrap;
overflow-x: auto;
}
}
.docs-demo__code__mobi__content {
iframe {
width: 100%;
border: 0;
height: 548px;
}
}
</style>
然后在main.js
中引用DemoBlock
组件
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
// 引入组件
import DemoBlock from './components/DemoBlock.vue'
import TestUi from "../src/index";
Vue.use(TestUi);
Vue.component('DemoBlock', DemoBlock)
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
重新运行项目就可以看到效果了。
5、现在效果应该都出来了,可以给代码添加高亮,使其更漂亮。
npm i highlight.js -S
再在main.js中添加如下配置,然后代码就能语法高亮了!
import 'highlight.js/styles/vs.css'