前言:

开发vue组件库需要提供组件的使用文档,最好是有渲染到浏览器的demo实例,既能操作又能查看源代码。markdown作为常用的文档编写载体,如果能在里面直接写vue组件,同时编写使用说明就再好不过。流行的组件库element-ui的文档就是用markdown写出来的,看了看其处理md的程序后,自己也决定写一个类似的处理程序,研究一下其中的细节。

技术点

1.markdown-it

处理markdown最常用的工具是markdown-it,它能把我们写的markdown文件转换为html。类似于babel,markdown也有自己的插件系统,通过设置或者编写自定义插件改变渲染的路径。

2.webpack-loader

处理md文件可以使用自定义webpack-loader来处理,先把md内容转为合适html,然后再给vue-loader处理。

3.cheerio

使用markdown-it把md内容转为html之后,需要操作html,cherrio以类似jquery的方式操作html,简单方便。

4.hljs

代码需要高亮渲染,hijs的功能就是将代码处理成html,通过样式使其高亮显示出来。

步骤

1.配置webpack解析md

{
test: /\.md$/,
use:[
{loader: 'vue-loader'},
{ loader: path.resolve(__dirname,'./markdown-loader/index.js') }
]
},

2.markdown-loader的入口

module.exports = function (source) {
this.cacheable && this.cacheable();
const {resourcePath=''} = this
const fileName = path.basename(resourcePath,'.md')
// @符号在markdown中是特殊符号
source = source.replace(/@/g, '__at__'); var content = parser.render(source).replace(/__at__/g, '@'); var result = renderMd(content,fileName) return result
};

3.添加插件markdown-it-container

markdown-it-container是一个插件,使用这个插件之后就可以在markdown中添加自己的标识,然后就能自定义处理标识里面的内容。在这里可以在把代码块放到标识内部,主要是防止markdown-it把vue组件转成html,由自己处理这些代码,最终返回想要的内容。

::: demo
​```html
<i class="kv-icon-close fs-24"></i>
<i class="kv-icon-link fs-24"></i>
​```
:::

上面就是插件的用法,demo由自己定义,初始注入的代码如下:

parser.use(require('markdown-it-container'), 'demo', {
validate(params) {
return params.trim().match(/^demo\s*(.*)$/);
},
// 把demo代码放到div.kv-demo里面
render(tokens, idx) {
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
if (tokens[idx].nesting === 1) {
const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : '';
// 先把demo中的代码放到demo-block的之中,然后程序继续render fence,按照上面的fence规则渲染出代码部分,作为隐藏的查看代码。
return `<demo-block><div class="kv-demo">${content}</div>`;
}
return '</demo-block>';
}
})

render方法仿照的是npm包里的例子。其中的tokens是AST节点,可以从这个网址看到markdown-it解析的AST,对照着做判断。

根据自己的理解,因为html是有起始标签和结束标签,markdown-it的render也是成对的,也就是在标记的起始和结束都会调用render方法,所以在demo起始的时候返回了一个起始<demo-block> (这是个全局定义的vue组件),然后把代码放到内部;

markdown会继续处理demo标识内部``` 标识,这个标识在markdown-it中有自己的rules (rules.fence)来处理;然而我们的目标是把这个代码放到一个标签中渲染成html,然后作为查看源码的部分展示出来,这就需要自定义了:

// 先保存下来
const defaultRender = parser.renderer.rules.fence;
parser.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">${hljs.highlight('html',token.content.replace(/^(\s*)|(\s*)$/g,'')).value}</code></pre>
</template>`;
} return `<div class="code-common">${defaultRender(tokens, idx, options, env, self)}</div>`
};

需要注意的是如果初始化parser的时候如果配置了 highlight: renderHighlight,调用defaultRender的时候会自动处理成高亮;否则需要就需要自己处理了,就是上面的hljs.highlight('html',token.....

做完以上部分之后,md的内容会被渲染成代码片断,内部包含普通的html标签和vue组件标签,大概如下:

<div>一些文字</div>
<demo-block>
<div class="kv-demo">
<ul class="icon-list">
<li v-for="name in icons" :key="name">
<span>
<i :class=" iconPre+ name"></i>
{{'kv-' + name}}
</span>
</li>
</ul>
<script>
export default {
data() {
return {
icons: require('../icon.json'),
iconPre:'kv-icon-'
};
}
}
</script> <style lang="scss">
.demo-icon {
.....
}
</style>
</div>
<template slot="highlight">
......
</template>
</demo-block>

组装成vue模板

这个代码和vue的组件的代码不一致,是无法解析的,需要修正一下。

另外一篇文档中会有多个demo即多个export default,解决方案就是把各个demo提取成组件,注册当前文档这个vue组件中,把demo的部分替换组件的名字。

第一部分:组装当前文档为vue组件 ,同时挂载提取出来demo组件https://github.com/blank-x/kv/blob/master/build/markdown-loader/index.js#L15

var renderMd = function (html,fileName) {
......
}

第二部分:提取其中的demo为组件,https://github.com/blank-x/kv/blob/master/build/markdown-loader/index.js#L57)

var renderVueTemplate = function (content) {
......
}

结果类似于如下:

<template>
<div class="demo-">
<demo-block>
<template slot="source">
<kv-demo0></kv-demo0>
</template>
<template slot="highlight">
<pre v-pre><code class="html">......</code></pre>
</template>
</demo-block>
.......
<demo-block>
<template slot="source">
<kv-demo1></kv-demo1>
</template>
<template slot="highlight">
<pre v-pre><code class="html"><span class="hljs-tag">.......</code></pre>
</template>
</demo-block>
</div>
</template>
<script>
export default {
name: "component-doc0",
components: {
"kv-demo0": {
template: `<div class="kv-demo0"><kv-tag>标签一</kv-tag></div>`
},
"kv-demo1": {
template: `<div class="kv-demo1">
<kv-tag :key="tag.name" v-for="tag in dynamicTags"
closable :disable-transitions="false" @close="handleClose(tag)" :type="tag.color">
{{tag.name}}
</kv-tag>
</div>`,
data() {
return {
dynamicTags: [{
name: "标签一",
color: "primary"
}]
};
},
methods: {
handleClose(tag) {
this.dynamicTags.splice(this.dynamicTags.indexOf(tag), 1);
}
}
}
}
};
</script>
<style lang="scss" >
.kv-tag {
margin-right: 8px;
}
</style>

组件kv-demo0 和kv-demo1 在components中定义;

在demo内部的scss会被提出来,放到了外层vue组件中,如果需要修改样式,可以参考如下写法:

.demo-tag .kv-demo1{
//
}
.demo-tag .kv-demo0{
//
}
tag // md的名字
demo0 // 页面内第几个demo

未解决的问题

每一个demo中script标签和export之间的代码被丢弃。如果需要引入其他文件,可以在data中通过require引入;

最后

本代码仅为练手使用,未在实际开发中使用,如有不正之处望指正。

vue组件库用markdown生成文档的更多相关文章

  1. 使用Ldoc给Lua生成文档

    Ldoc介绍 LDoc是一个Lua的文档生成工具,过去,比较常用的Lua生成文档的工具是LuaDoc,可惜作者自从2008年之后就再也没有发布过新的版本了,说明作者基本上已经放弃维护了.而LDoc则是 ...

  2. SpringBoot 集成Swagger2自动生成文档和导出成静态文件

    目录 1. 简介 2. 集成Swagger2 2.1 导入Swagger库 2.2 配置Swagger基本信息 2.3 使用Swagger注解 2.4 文档效果图 3. 常用注解介绍 4. Swagg ...

  3. 使用 Swagger 自动生成 ASP.NET Core Web API 的文档、在线帮助测试文档(ASP.NET Core Web API 自动生成文档)

    对于开发人员来说,构建一个消费应用程序时去了解各种各样的 API 是一个巨大的挑战.在你的 Web API 项目中使用 Swagger 的 .NET Core 封装 Swashbuckle 可以帮助你 ...

  4. MVC WEB api 自动生成文档

    最近在一直在用webapi做接口给移动端用.但是让我纠结的时候每次新加接口或者改动接口的时候,就需要重新修改文档这让我很是苦恼.无意中发现.webapi居然有自动生成文档的功能....真是看见了救星啊 ...

  5. newlisp 注释生成文档

    最近写了一个newlisp_armory库,用来实现一些newlisp自身不支持的操作.比如跨windows和ubuntu的目录拷贝功能等. 自己用的时候,发现没有API reference文档参考, ...

  6. Vue组件库的那些事儿,你都知道吗?

    前段时间一直在研究Vue组件库,终于在组内派上了用场.来给大家贡献一篇关于Vue组件库的相关知识.经验不多,如果有不合理的地方还请多多指出哦--- 回想一下,在你们公司或者你们小组是否有一个以上的项目 ...

  7. JavaScript 实现命名空间(namespace)的最佳方案——兼容主流的定义类(class)的方法,兼容所有浏览器,支持用JSDuck生成文档

    作者: zyl910 一.缘由 在很多的面向对象编程语言中,我们可以使用命名空间(namespace)来组织代码,避免全局变量污染.命名冲突.遗憾的是,JavaScript中并不提供对命名空间的原生支 ...

  8. 使用Sphinx为你的python模块自动生成文档

    Sphinx是一个可以用于Python的自动文档生成工具,可以自动的把docstring转换为文档,并支持多种输出格式包括html,latex,pdf等. 安装 创建一个sphinx项目 下面的命令会 ...

  9. 仿ElementUI构建自己的Vue组件库用babel-plugin-component按需加载组件及自定义SASS主题

    最近使用ElementUI做项目的时候用Babel的插件babel-plugin-component做按需加载,使得组件打包的JS和CSS包体积大大缩小,加载速度也大大提升,所有想模仿做一个组件库也来 ...

随机推荐

  1. 有return的情况下try_catch_finally的执行顺序

    java异常处理之try_catch_finally 看下面的一个列子: public class TestException { int goabl=1; public TestException( ...

  2. 苹果XR手机的音频体验测试总结

    苹果XR手机的音频   苹果XR算是苹果手机历史上一个里程碑的型号了,是苹果憋了两年的大招,连苹果9的称号就不要了.直接是X.说明苹果对它给予的希望很大.作为一个音频算法工程师,一直想体验一下XR的音 ...

  3. BootstrapDialog.confirm确认对话框

    1. 依赖文件: bootstrap.js bootstrap-dialog.js bootstrap.css bootstrap-dialog.css 2.代码 BootstrapDialog.co ...

  4. nginx使用热部署添加新模块

    简介 当初次编译安装nginx时,http_ssl_module 模块默认是不编译进nginx的二进制文件当中,如果需要添加 ssl 证书.也就是使用 https协议.那么则需要添加 http_ssl ...

  5. JS代码简洁之道--函数

    函数的参数越少越好 有一个准则是:如果你的函数参数超过两个,就应该改为对象传入. 这样做是合理的,因为当函数参数超过两个时,参数顺序开始变得难以记忆,而且容易出现一种很尴尬的情况:比如我只需要传入第三 ...

  6. tkinter的三种几何布局管理类

    1.pack() 主要采用块的方式组织子组件 如下: import tkinter root=tkinter.Tk() #创建窗口对象 label=tkinter.Label(root,text='h ...

  7. python数据结构(三)

    copy 复制对象,copy模块包含了两个行数copy和deepcopy,用于复制现有的对象. 浅副本(浅复制) copy()创建的浅副本是一个新容器,其中填充了原对象内容的引用 import cop ...

  8. LeetCode题解【题2】:两数相加

    原题链接:https://leetcode-cn.com/problems/add-two-numbers/ 查看请另起链接打开. 解题思路执行用时 :2 ms, 在所有 Java 提交中击败了99. ...

  9. 集训之各种dp

    1.线性 「BZOJ1609」麻烦的聚餐 分别求一遍连续非下降/上升子序列长度,用总长减去,取最小值即可,主要\(O(n^2)\)优化 Code #include <cstdio> #in ...

  10. 洛谷 P2607 [ZJOI2008]骑士 树形DP

    题目描述 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各 界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里, ...