手把手做一个基于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:/ ...
随机推荐
- Sentinel滑动窗口算法
在前面搞清楚了Sentinel的使用后,大致理了一下Sentinel的责任链,搞清楚了这个,基本就已经梳理清楚sentinel-core模块的大部分内容,顺着这条链路可以继续梳理很多东西. 知其然.知 ...
- 单细胞分析实录(5): Seurat标准流程
前面我们已经学习了单细胞转录组分析的:使用Cell Ranger得到表达矩阵和doublet检测,今天我们开始Seurat标准流程的学习.这一部分的内容,网上有很多帖子,基本上都是把Seurat官网P ...
- java使用正则的例子
package com.accord.util; import java.util.ArrayList; import java.util.List; import java.util.regex.M ...
- 总结(2019CSP之后),含题解
从\(\mathcal{CSP}\) 爆炸 到现在,已经有\(3\)个月了.这三个月间,我--这个小蒟蒻又接触了许多听不懂的东西 \(\mathcal{No.}1\) 字符串\(\mathcal{ha ...
- 如何快速搭建hadoop集群
安装好虚拟机,重命名为master 配置网卡 命令:vi /etc/sysconfig/network-scripts/ifcfg-en(按tab键) 这里要配置ip,网关,域名解析 例如我的 IPA ...
- htaccess在线生成工具用法大全 (转)
对于一个不懂程序的SEOER来做,更改代码方面是一件非常苦难的事情,当我们遇到301转向以及404页面的制作问题时,经常会困恼我们,这里我提供一个htaccess在线生成工具,这里有404页面链接生成 ...
- Cocos Creator 新资源管理系统剖析
目录 1.资源与构建 1.1 creator资源文件基础 1.2 资源构建 1.2.1 图片.图集.自动图集 1.2.2 Prefab与场景 1.2.3 资源文件合并规则 2. 理解与使用 Asset ...
- Label_strange_labels
空格符号 特殊符号名 注释语句 , align = "left right center" 粗体 斜体 也可能是控制标签 上标 下标 大字号 小字号 下划线 删除线 等宽 键盘输 ...
- idea启动build过慢
原文链接http://zhhll.icu/2020/04/17/idea/idea%E4%B9%8B%E7%BC%96%E8%AF%91%E9%97%AE%E9%A2%98/ 之前使用idea的时候每 ...
- Dota游戏匹配的所有组合
在Dota游戏中有一种匹配玩法,任意5人以下玩家组队,加入匹配系统,由系统组合出5人 vs 5人的组合进行游戏,比如2人+3人 vs 1人+4人.抽象出这个问题,就变成两边各有m个玩家,最多允许n个 ...