这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战html
Vuepress
Element
的源码找出了我认为的一种最优解,借此机会分享一下先把最后的成品发出来给你们看看vue
在上篇中分析概括了一下具体的步骤(以下),接下来咱们就来具体的实现一下webpack
demo
而且统同样式Markdown
自定义容器来编写demo
代码md
使用自定义容器来实现上文效果markdown
渲染方法使咱们输入的代码块能够输出内容为符合Vue Template
语法的代码块vue
代码后交由Vuepress
的vue-loader
处理编译为文档docs
├─ .vuepress
│ ├─ components
│ │ └─ demoBlock.vue
│ ├─ config.js
│ └─ enhanceApp.js
└─ component
└─ basic
└─ button.md
复制代码
Vuepress
能够自动识别components
里面的组件并注册因此咱们在里面建一个通用组件demoBlock
用于展现demo
Element
的通用组件后观察到这个组件主要由三部分组成:组件示例
、描述
、组件代码块
/* demoBlock.vue */
<template>
<div class="block">
<div class="demo-content">
<!-- 插入组件 -->
<slot name="demo"></slot>
</div>
<div class="meta" ref="meta">
<div class="description">
<!-- 插入描述信息 -->
<slot name="description"></slot>
</div>
<div class="code-content">
<!-- 插入代码块 -->
<slot name="source"></slot>
</div>
</div>
</div>
</template>
复制代码
组件示例
、描述
、组件代码块
,这样咱们就能够经过在md
转vue
的时候根据特别的插槽来组装咱们的组件示例对于自定义组件咱们可使用markdown-it-container 参考官网构建git
/* containers.js */
const mdContainer = require('markdown-it-container');
module.exports =md => {
//将markdown-it-container插件加载到当前的解析器实例中
md.use(mdContainer, 'demo', {
validate(params) {
//函数在开始标记后验证尾部,成功时返回true
return params.trim().match(/^demo\s*(.*)$/);
},
render(tokens, idx) {
//渲染器函数
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
if (tokens[idx].nesting === 1) {
const description = m && m.length > 1 ? m[1] : '';
// opening tag
return `<demo-block>
<div slot="demo">组件demo</div>
<div slot="description">${description}</div>
<div slot="source">代码块</div>`
} else {
// closing tag
return `</demo-block>`;
}
}
});
}
复制代码
markdown-it-container
支持两个参数,第一个是自定义容器的名字,第二个是一些选项github
true
成功时应返回:
),在分隔符中使用的字符在render
方法中也有两个参数web
token
们的列表token
的索引值得一提的是token的两个属性npm
1
表示打开 0
表示自动关闭 -1
表示正在关闭在选项中的render
能够对自定义的容器作渲染处理,像上面我就让识别到demo
的自定义容器渲染成这个格式,那么咱们在md
文件输入自定义容器时就会找到对应的组件进行渲染api
建立完自定义容器后咱们要组装到Vuepress
的配置中,Vuepress
自带了chainMarkdown来修改内部的markdown
配置,具体的配置操做能够参考配置插件markdown
/* config.js */
module.exports = {
title: 'Zylw-Ui',
description: '开始你的组件化之旅吧~',
...
plugins: [
[
require('./md-loader')
]
]
...
}
复制代码
/* index.js */
const demoBlockContainers = require('./common/containers')
module.exports = () => {
return {
chainMarkdown(config) {
//修改内部的 markdown 配置
config // 增长额外的插件markdownContainers
.plugin('markdownContainers')
.use(demoBlockContainers)
.end();
}
}
}
复制代码
尝试在md
文件中使用后效果就出来了编辑器
此时的结构是这样的
docs
├─ .vuepress
│ ├─ components
│ │ └─ demoBlock.vue
│ ├─ config.js
│ ├─ enhanceApp.js
│ └─ md-loader
│ ├─ common
│ │ └─ containers.js
│ └─ index.js
└─ component
└─ basic
└─ button.md
复制代码
大致效果架子出来了以后,咱们就要考虑如何将自定义容器里的内容分别输出到组件位置
和代码块位置
,一个典型的单文件组件包括三块:template
script
与 style
,那么接下的重点就是如何拼凑出template
与script
的内容
咱们能够参考element
的作法,因为代码太长,先放上Element的源码能够一块儿食用 能够看到Element在渲染的时候加入了一个占位符来接受咱们的代码块,再经过编译的时候对这个注释块进行处理就能够分别转化到
template
script
与 style
这时咱们就要改写一下咱们的结构(此代码灵感来自Demo Container)
/* containers.js */
const mdContainer = require('markdown-it-container');
module.exports =md => {
//将markdown-it-container插件加载到当前的解析器实例中
md.use(mdContainer, 'demo', {
validate(params) {
//函数在开始标记后验证尾部,成功时返回true
return params.trim().match(/^demo\s*(.*)$/);
},
render(tokens, idx) {
//渲染器函数
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
if (tokens[idx].nesting === 1) {
const description = m && m.length > 1 ? m[1] : '';
const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : '';
return `<demo-block>
<template slot="demo"><!--pre-render-demo:${content}:pre-render-demo--></template>
${description ? `<div slot="description">${md.render(description).html}</div>` : ''}
<template slot="source">
`;
}
return `</template></demo-block>`;
}
})
}
复制代码
containers.js
截取类型为fence
的代码块放到占位符中render.js
对占位符的内容进行处理 具体代码Vuepress
的 extendMarkdown API 继续拓展了其内部的markdown
对象,修改内部用于渲染markdown
文件的 markdown-it实例的配置/* index.js */
const renderDemoBlock = require('./common/render')
module.exports = () => {
return {
...
extendMarkdown: md => {
//修改内部用于渲染 markdown 文件的 markdown-it实例的配置
const id = setInterval(() => {
const render = md.render;
if (typeof render.call(md, '') === 'object') {
md.render = (...args) => {
let result = render.call(md, ...args);
//分别提取三大块进行拼接
const { template, script, style } = renderDemoBlock(result.html);
result.html = template;
result.dataBlockString = `${script}\n${style}\n${result.dataBlockString}`;
return result;
}
clearInterval(id);
}
}, 10);
}
}
}
复制代码
demo
就大功告成啦!!!对比以前冗余写法是否是方便特别多呢,接下来只须要在demoBlock.vue
更改属于本身的样式就能够啦docs
├─ .vuepress
│ ├─ components
│ │ └─ demoBlock.vue
│ ├─ config.js
│ ├─ enhanceApp.js
│ └─ md-loader
│ ├─ common
│ │ ├─ render.js
│ │ ├─ util.js
│ │ └─ containers.js
│ └─ index.js
└─ component
└─ basic
└─ button.md
复制代码
markdown-it-container
自定义容器的方法结合vue-template-compiler
将代码片断转换成组件,不一样的文档编辑器可能有不一样的办法但原理都是相同的要么经过自身支持的插件进行配置要么经过Webpack
进行配置markdown
转html
的一些原理,因此仍是那句话多看源码真的颇有用