手把手做一个基于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:/ ...
 
随机推荐
- 【剑指offer】01 二维数组中的查找
			
题目地址:二维数组中的查找 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照 ...
 - Asp.net Core3.1+Vue 使用SignalR推送数据
			
本文就简单使用 往前端页面推送消息 SignalR 是什么 SignalR是一个.NET Core/.NET Framework的开源实时框架. SignalR的可使用Web Socket, Serv ...
 - webform中配置服务器控件的样式
			
前台 Style <asp:Label ID="Label1" runat="server" Text="Label" Style=& ...
 - [Machine Learning] 逻辑回归 (Logistic Regression) -分类问题-逻辑回归-正则化
			
在之前的问题讨论中,研究的都是连续值,即y的输出是一个连续的值.但是在分类问题中,要预测的值是离散的值,就是预测的结果是否属于某一个类.例如:判断一封电子邮件是否是垃圾邮件:判断一次金融交易是否是欺诈 ...
 - 新下载的Chrome 不能用,设置搜索引擎,谷歌浏览器不能用,chrome浏览器不能用,google chrome 不能用
			
新下载的chrome默认搜索引擎 是google搜索,而google搜索引擎在国内是不能使用的,要设置为 百度或.360.搜狗搜索引擎才能使用. 设置方法如下: 1.打开 Chrome. 2.点击右上 ...
 - Kali实现靶机远程控制
			
任务一.使用netcat建立监听并连接 1. 在ubuntu开启本地监听,在kali中连接 2. 在kali中开启监听,在ubuntu中反弹连接 任务二.使用met ...
 - Spring boot JPA读取数据库方法
			
方法1: 1 StringBuffer sb = new StringBuffer(300); 2 sb.append("SELECT v.id, v.container_number, v ...
 - intel英特尔NUC主机bug大清除案例
			
如果你的NUC进入HDMI无法在显示器显示,可以参考此文章的思路,尝试排除各种问题.接下来我讲述一下我的NUCbug清除历程. 我的NUC激动时刻 我的NUC是这个型号,直接上图了:英特尔(Intel ...
 - JavaScript高级程序设计(第4版)知识点总结
			
介绍 JavaScript高级程序设计 第四版,在第三版的基础上添加了ES6相关的内容.如let.const关键字,Fetch API.工作者线程.模块.Promise 等.适合具有一定编程经验的 W ...
 - 2021新年 Vue3.0 + Element UI 尝鲜小记
			
2021年,又是新的一年,Vue 已经发布 3.0 版本,最好用的 UI 组件库 Element UI 也发布了适配 Vue3.0 的新版本,是时候开始学习一下这两个新技术. 本文主要记录了使用 Vu ...