手把手做一个基于vue-cli的组件库(下篇)
<template>
<div id="app">
<router-view />
</div>
</template>
2、因为文档是用markdown写的,需要项目能识别markdown组件。
const path = require('path')
module.exports = {
// 修改 pages 入口
pages: {
index: {
entry: 'examples/main.js', // 入口
template: 'public/index.html', // 模板
filename: 'index.html' // 输出文件
}
},
parallel: false,
// 扩展 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
})
}
}
4、创建文档的目录及文件。
在 examples 目录下创建 docs 文件夹,在docs文件夹下创建 test1.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>
``` :::
将 test1.md 添加进路由进行测试
在router/index.js中添加
{
path: '/test1',
name: 'test1',
component: () => import(/* webpackChunkName: "about" */ '../docs/test1.md')
}
重新运行测试:如果没有报错,页面能正确显示 test1.md 内的文本,这一步就算成功了。
const path = require('path')
const md = require('markdown-it')() // 引入markdown-it
module.exports = {
// 修改 pages 入口
pages: {
index: {
entry: 'examples/main.js', // 入口
template: 'public/index.html', // 模板
filename: 'index.html' // 输出文件
}
},
parallel: false,
// 扩展 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) => {
// 对于代码块去除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: [
// :::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']
]
})
}
}
<template>
<div class="demo-block" :class="blockClass">
<!-- 源码运行 -->
<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="text-slide">
<span>{{ controlText }}</span>
</transition>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isExpanded: false,
fixedControl: false,
scrollParent: null
}
}, computed: {
lang() {
return this.$route.path.split('/')[1]
}, blockClass() {
return `demo-${this.lang} demo-${this.$router.currentRoute.path
.split('/')
.pop()}`
},
controlText() {
return this.isExpanded ? '隐藏代码' : '显示代码'
},
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'
console.log(this.$el.getElementsByClassName('description').length)
console.log(this.$el.getElementsByClassName('highlight'))
console.log(this.codeAreaHeight)
if (!val) {
this.fixedControl = false
this.$refs.control.style.left = '0'
}
}
}
}
</script>
<style lang="scss">
.demo-block {
width: 60%;
padding: 8px 16px;
margin: auto;
margin-top: 10px;
border-left: solid 5px#fc297f;
background-color: #f8d1db;
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);
}
.meta {
margin-top: 10px;
background-color: #fafafa;
border-radius: 8px;
overflow: hidden;
height: 0;
transition: height 0.2s;
} .description {
box-sizing: border-box;
border-radius: 3px;
font-size: 14px;
line-height: 22px;
color: #666;
word-break: break-word;
margin: 10px;
background-color: #fff;
p {
width: 100%;
}
}
.demo-block-control {
border-top: solid 1px #eaeefb;
height: 44px;
box-sizing: border-box;
background-color: #fff;
border-radius: 8px;
text-align: center;
margin-top: 10px;
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: #fc297f;
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>
import DemoBlock from './components/DemoBlock.vue'
Vue.component('DemoBlock', DemoBlock)
html,
body {
margin: 0;
padding: 0;
height: 100%;
background-color: #17171d;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei', SimSun, sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent; &.is-component {
overflow: hidden;
}
} #app {
height: 100%;
&.is-component {
overflow-y: hidden;
.main-cnt {
padding: 0;
margin-top: 0;
height: 100%;
min-height: auto;
}
.headerWrapper {
position: fixed;
width: 100%;
left: 0;
top: 0;
z-index: 1500;
.container {
padding: 0;
}
}
}
} a {
color: #409eff;
text-decoration: none;
} code {
padding: 0 4px;
border: 1px solid #eaeefb;
border-radius: 4px;
} button,
input,
select,
textarea {
font-family: inherit;
font-size: inherit;
line-height: inherit;
color: inherit;
} .hljs {
line-height: 20px;
font-family: Menlo, Monaco, Consolas, Courier, monospace;
font-size: 12px;
padding: 10px 24px 18px 24px;
border: solid 1px #eaeefb;
border-radius: 4px;
-webkit-font-smoothing: auto;
} .main-cnt {
margin-top: -80px;
padding: 80px 0 340px;
box-sizing: border-box;
min-height: 100%;
} #app {
h2 {
font-size: 28px;
color: #fc297f;
margin: 0;
}
h3 {
font-size: 22px;
}
h2,
h3,
h4,
h5 {
width: 60%;
margin: auto;
margin-top: 10px;
font-weight: normal;
color: #fc297f;
a {
display: none;
}
&:hover a {
opacity: 0.4;
}
} p {
width: 60%;
margin: auto;
padding: 10px;
font-size: 16px;
color: #d3aec2;
line-height: 30px;
} .tip {
width: 60%;
margin: auto;
margin-top: 10px;
padding: 8px 16px;
background-color: #ecf8ff;
border-radius: 4px;
border-left: #1b9ae4 5px solid;
p {
width: 100%;
}
code {
background-color: rgb(255, 255, 255);
color: #445368;
}
} .warning {
width: 60%;
margin: auto;
margin-top: 10px;
padding: 8px 16px;
background-color: #fff6f7;
border-radius: 4px;
border-left: rgb(252, 122, 2) 5px solid;
p {
width: 100%;
}
code {
background-color: rgba(255, 255, 255, 0.7);
color: #445368;
}
}
}
@media (max-width: 1140px) {
.container,
.page-container {
width: 100%;
}
} @media (max-width: 768px) {
.container,
.page-container {
padding: 0 20px;
} #app.is-component .headerWrapper .container {
padding: 0 12px;
}
}
import './assets/common.scss' // 公共样式
import hljs from 'highlight.js'
import 'highlight.js/styles/monokai-sublime.css' router.afterEach(() => {
Vue.nextTick(() => {
const blocks = document.querySelectorAll('pre code:not(.hljs)')
Array.prototype.forEach.call(blocks, hljs.highlightBlock)
})
})
:::demo 这里贴出的是源码,刷新可重播。 ```html
<template>
<div>
<div>测试</div>
</div>
</template> <script></script>
<style></style>
``` :::
*.sh
node_modules
lib
coverage
*.md
*.scss
*.woff
*.ttf
aui-web
build
[
{
"name": "test",
"groups": [
{
"groupName": "测试组件",
"list": [
{
"path": "/test1",
"title": "测试1"
},
{
"path": "/test2",
"title": "测试2"
}
]
}
]
}
]
// export default router
import Vue from 'vue'
import Router from 'vue-router' import navConfig from './routerCon' Vue.use(Router)
const docsRoutefun = navConfig => {
const route = []
navConfig.forEach(item => {
if (item.groups) {
item.groups.forEach(group => {
group.list.forEach(nav => {
route.push({
path: nav.path,
name: nav.name,
component: r =>
require.ensure([], () => r(require(`@/docs${nav.path}.md`)))
})
})
})
} else {
route.push({
path: item.path,
name: item.name,
component: r =>
require.ensure([], () => r(require(`@/docs${item.path}.md`)))
})
}
})
return route
}
const docsRoute = docsRoutefun(navConfig)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [{ path: '/', redirect: '/test1' }, ...docsRoute]
})
<template>
<div id="app">
<div class="main">
<!-- sidebar -->
<div class="sidebar">
<menuCom :data="navsData"></menuCom>
</div>
<div class="view page-container">
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
import menuCom from './components/menuCom'
import navsData from './router/routerCon.json'
export default {
components: {
menuCom
},
data() {
return {
navsData
}
}
}
</script>
<style lang="scss">
html,
body {
margin: 0;
height: 100%;
background-color: #17171d;
}
.header {
top: 0;
height: 60px;
background-color: #121217;
}
.footer {
position: absolute;
height: 60px;
background-color: antiquewhite;
width: 100%;
}
.footer {
bottom: 0;
}
.main {
background-color: #17171d;
position: absolute;
bottom: 0;
top: 60px;
width: 100%;
overflow: hidden;
}
.sidebar,
.view {
overflow: auto;
}
.sidebar {
float: left;
height: 100%;
width: 200px;
padding: 10px 0 10px 0;
border-right: #000000 3px solid;
}
.view {
padding: 0 0 80px 0;
float: left;
height: calc(100% - 50px);
width: calc(100% - 203px);
overflow: auto;
}
</style>
手把手做一个基于vue-cli的组件库(下篇)的更多相关文章
- 手把手做一个基于vue-cli的组件库(上篇)
基于vue-cli4的ui组件库,先贴个最终效果吧,步骤有点多,准备分上下篇,上篇:如何做一个初步的组件.下篇:编写说明文档及页面优化.开工. GitHub源码地址:https://github.co ...
- 基于Vue的npm组件库
前言(*❦ω❦) 思维导图可能有点高糊,有点太大了,项目和导图文件放到github或giteee上,这个思维导图也是我文章的架构,思维导图是用FeHelper插件生成的,这个是一款开源chrome插件 ...
- 使用webpack4搭建一个基于Vue的组件库
组内负责的几个项目都有一些一样的公共组件,所以就着手搭建了个公共组件开发脚手架,第一次开发 library,所以是参考着 iview 的配置来搭建的.记录如何使用webpack4搭建一个library ...
- 新建一个基于vue.js+Mint UI的项目
上篇文章里面讲到如何新建一个基于vue,js的项目(详细文章请戳用Vue创建一个新的项目). 该项目如果需要组件等都需要自己去写,今天就学习一下如何新建一个基于vue.js+Mint UI的项目,直接 ...
- 一个基于vue的时钟
前两天写了一个基于vue的小钟表,给大家分享一下. 其中时针和分针使用的是图片,结合transform制作:表盘刻度是通过transform和transformOrigin配合画的:外面的弧形框框,啊 ...
- 基于vue项目的组件中导入mui框架初始化滑动等效果时需移除严格模式的问题
基于vue项目的组件中导入mui框架初始化滑动等效果时,控制台报错:Uncaught TypeError: 'caller', 'callee', and 'arguments' properties ...
- 扩展一个boot的插件—tooltip&做一个基于boot的表达验证
在线演示 本地下载 (代码太多请查看原文) 加班,加班加班,我爱加班··· 我已经疯了,哦也. 这次发一个刚接触boot的时候用boot做的表单验证,我们扩展一下tooltip的插件,让他可以换颜色. ...
- 一个基于vue的仪表盘demo
最近写了一个基于vue的仪表盘,其中 主要是和 transform 相关的 css 用的比较多.给大家分享一下,喜欢的话点个赞呗?嘿嘿 截图如下: 实际效果查看地址:https://jhcan333. ...
- 一个基于swoole的作业调度组件,已经实现了redis和rabitmq队列消息存储。
https://github.com/kcloze/swoole-jobs 一个基于swoole的作业调度组件,已经实现了redis和rabitmq队列消息存储.参考资料:swoole https:/ ...
随机推荐
- Redis 6.0 多线程性能测试结果及分析
单线程的Redis一向以简洁高效著称,但也有其阿喀琉斯之踵:阻塞!单个线程在最容易产生瓶颈的网络读写(Redis大key)请求完成之前,其他所有请求都将会被阻塞,严重影响其效率,因此Redis的多 ...
- 关于Git的一些常规操作
最近刚换了新的办公电脑,Git有重新安装了一遍,很多步骤久了不操作就忘了,又是好一顿折腾,于是这次就顺便记下来了. 不错的Git教程: https://www.liaoxuefeng.com/wiki ...
- Spring中的注解@Value("#{}")与@Value("${}")的区别
1 @Value("#{}") SpEL表达式 @Value("#{}") 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法.当然还有可 ...
- 一个简单的java项目使用hibernate连接mysql数据库
实体类与表对应文件Customer.hbm.xml <?xml version="1.0" encoding="UTF-8"?><!DOCTY ...
- Java编译执行过程
在刷软件设计师中级考试的题目,判断关于编译系统对某高级语言进行翻译的叙述的对错.记得刚开始学Java的时候自己就觉得自己对程序的执行过程理解的相当的透彻,但是一对答案,我的小心脏就有点受不了了,特此在 ...
- 使用pdf.js aspose各种文档转PDF 版本对应license.xml 去水印破解
在使用pdf.js途中,使用aspose转换的文件一直有水印,在网上找了许多破解办法都是已经失效的,于是乎,就查看了一下jar的源码,找到了版本对应的破解字符(如下):对应版本为 aspose-wor ...
- 一行 CSS 代码的魅力
之前在知乎看到一个很有意思的讨论 一行代码可以做什么? 那么,一行 CSS 代码又能不能搞点事情呢? CSS Battle 首先,这让我想到了,年初的时候沉迷的一个网站 CSS Battle .这个网 ...
- 编写 Dockerfile 生成自定义镜像
一般情况下我们可以从公共渠道诸如 DockerHub 获取镜像上获取镜像,但是在实际生产过程中,往往需要定制化的镜像,例如修改一些配置文件,增加一些特殊的命令或软件等需求,这时就需要通过编写 Dock ...
- 小米11和iphone12参数对比哪个好
小米11:搭载最新一代三星的AMOLED屏幕,120Hz屏幕刷新,iPhone12使用全新一代的视网膜屏,6.1英寸屏幕,支持60Hz屏幕刷新,支持HDR显示,P3广色域小米手机爆降800 优惠力度空 ...
- 字典实现:python-----VS----java
对比python和java的字典数据结构,以下就LeetCode面试题 17.10. 主要元素为栗子,进行学习.是一道简单题目,重点看数据结构的运用与实现. 普通的思路,使用一个字典结构记录每个元素出 ...