前言✍️
- 最近在搭自己的组件库,关于文档用的是
Vuepress
- 官网在文档说明展示组件示例的方法有很多种,但种种都不合心意
- 通过查阅网上的资料和
Element
的源码找出了我认为的一种最优解,借此机会分享一下 - 该篇主要是具体的实现,如果想看分析的可以看到如何优雅的使用Vuepress编写组件示例(上)
实际效果✔️
先把最后的成品发出来给大家看看
李性分析🙋♂️
在上篇中分析归纳了一下具体的步骤(如下),接下来我们就来具体的实现一下
- 第一步构建一个通用组件用于接收代码块来展示
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 参考官网构建
/* 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
支持两个参数,第一个是自定义容器的名字,第二个是一些选项
- name - 自定义容器的名字
- options:
- validate - 可选,打开标记后验证尾部的功能,
true
成功时应返回 - render - 可选,用于打开/关闭标记的渲染器函数
- marker - 可选 (
:
),在分隔符中使用的字符
在render
方法中也有两个参数
- tokens (Array) --
token
们的列表 - idx (Number) -- 用来渲染的
token
的索引
值得一提的是token的两个属性
- info -- 三个反引号后面跟的那个字符串
- nesting -- 标签状态:
1
表示打开0
表示自动关闭-1
表示正在关闭
在选项中的render
可以对自定义的容器做渲染处理,像上面我就让识别到demo
的自定义容器渲染成这个格式,那么我们在md
文件输入自定义容器时就会找到对应的组件进行渲染
创建完自定义容器后我们要组装到Vuepress
的配置中,Vuepress
自带了chainMarkdown来修改内部的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 复制代码
写在最后👋
- 当然如果不想自己重新配一个自定义容器的话也是有现成的插件的Demo Container
- 本文的分析思路灵感也是借鉴于这个插件,对于一些源码的分析也是看了这位博主的插件才受益匪浅
- 总的来说组件示例的呈现方式有很多,但是目前为止我觉得最好的是这种,通过
markdown-it-container
自定义容器的方法结合vue-template-compiler
将代码片段转换成组件,不同的文档编辑器可能有不同的办法但原理都是相同的要么通过自身支持的插件进行配置要么通过Webpack
进行配置 - 通过这次自己尝试配置也深入理解了一些
markdown
转html
的一些原理,所以还是那句话多看源码真的很有用 - 如果您觉得这篇文章有帮助到您的的话不妨🍉关注+点赞+收藏+评论+转发🍉支持一下哟~~😛