自定义md-loader来简单高效的维护组件文档
个人觉得,组件库最难的不是开发,而是使用,怎么才能让组内同事都用起来,这才是关键
背景
虽然现在开源的组件库很多,但每个项目里还是或多或少都会有人封装出一些项目内通用的基础组件、业务组件
我参与过多个项目,几乎每个项目都会存在这么一种现象:重复造轮子
同一个用途的组件被不同人多次实现,导致后续维护的人可能都不知道该用哪个好,或者干脆又自己撸了一个,就又恶性循环了
至于如何解决,遇到的基本就是强制定规范,但这种靠人为主观意识的约定,很容易松动,不长久
痛点
其实可以来分析下看看,为什么就会用不起来呢?
为什么大家乐意去用一些开源组件库,就是不想用项目里别人封装的呢?
就我个人而言,可能有这么几个原因:
- 我不知道原来项目里已经有这么个通用组件了
- 我找到组件代码,但我不确定这个组件呈现效果是什么,是不是我想要的,对业务不熟,与其慢慢去捞页面找试用,干脆自己再撸一个
- 我找到组件代码,也找到页面呈现效果,但我不知道该怎么使用,需要花时间去看源码
于是我反思,那我为什么会乐意去用开源组件库,比如 element-ui 组件呢:
- 官网可以直接找到所有组件呈现效果和示例代码
- 官网的配置项说明足够使用组件,而无需去看源码
所以对我来说,根源不是不想用同事封装的组件,而是懒得去看源码,去找示例
我更在意的是组件呈现效果和示例代码以及参数配置项说明
- 示例代码和参数配置项说明可以通过编写 md 文档来实现
- 组件呈现效果需要另外开发个 demo 组件来编写示例代码并运行
这意味着,封装一个组件,除了写文档,还需要再开发一个组件使用 demo,成本有些大,维护也麻烦
那么,有没有什么办法可以简化呢?
解决方案:自定义 md-loader
md-loader 是一个自定义的 webpack loader,用来解析 md 文件的,简单来说,它做了两件事:
- 将 md 解析成 vue 组件,以便 vue 项目里可以直接将 md 当作 vue 组件使用
- 自定义 md 语法
::: demo,以便达到只需在 md 中编写组件示例代码,解析后的 vue 代码会自动将组件示例代码运行起来,呈现真实效果
有了 md-loader 的这两个能力,我们可以再基于 require.context 搞个自动挂载组件路由
这样一来,我们只要在每个组件目录下搞个 README.md 文档,里面贴上组件示例代码,然后运行项目,打开组件路由就可以像使用 element-ui 组件官网一样来翻看我们的组件文档了
我们还可以再集成 monaco-editor 就可以实现一个简易的在线编辑调试代码的功能
如:在线体验下

上面示例中的组件使用说明文档内容,包括呈现效果和示例代码,全程都只需要在 md 文档里编写即可,而无需额外编写其他 demo 代码,如:
# 全局弹窗 this.$rgDialog
为了避免每次使用弹窗时需要编写分散各处的片段代码(el-dialog 的模板代码,控制显隐变量,显示关闭函数等),提取封装了挂载在全局函数的弹窗 `this.$rgDialog`
直接在点击事件方法里即可完成弹窗的相关代码
## 使用示例
::: demo
```vue
<template>
<div>
<el-button type="primary" @click="showDialog">点击显示弹窗</el-button>
</div>
</template>
<script>
import dialogContent from "@docs/使用说明.md";
export default {
data() {
return {};
},
mounted() {},
methods: {
showDialog() {
const rgDialog = this.$rgDialog({
props: {
title: "弹窗标题",
width: "80vw",
"close-on-click-modal": true
},
events: {},
content: dialogContent,
contentProps: {},
contentEvents: {
cancel: () => rgDialog.close()
}
}).show();
}
}
};
</script>
<style lang="scss" scoped></style>
```
:::
## options 参数说明
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| ------------- | ----------------------------------- | ------ | ------ | ----------------------------------------------------------- |
| props | el-dialog 的 props 输入参数 | object | — | {width: '700px', top: '5vh', 'close-on-click-modal': false} |
| events | el-dialog 的输出事件,如 @opened 等 | string | — | — |
| content | 弹窗内容的 vue 组件 | object | — | — |
| contentProps | 弹窗内容 vue 组件的 props 输入参数 | object | — | — |
| contentEvents | 弹窗内容 vue 组件的输出事件 | object | — | — |
## 方法
`this.$rgDialog()` 返回的弹窗实例对象的方法:
| 方法名 | 说明 | 参数 |
| ------ | -------- | ---- |
| show | 显示弹窗 | — |
| close | 关闭弹窗 | — |
md-loader 实现原理
这个 loader 是我前司一同事自己开发的,这是他的源码仓库和技术实现细节文档:
原理细节和源码可以移步到相关链接查看,这里简单概述下 md-loader 内部原理,一句话解释:
将 md 转成的 html 包裹到 vue 的 template 标签内,因此 md 可以直接被当作 vue 组件在代码里被引用,同时自定义扩展 md 的 ::: demo 语法,以便支持组件效果和示例代码可以呈现
loader 工作原理:
- 基于 markdown-it 系列插件将 md 转成 html
- 如果 md 里没有 ::: demo 场景,则直接将转成的 html 放到 vue 的 template 块里,交给 vue-loader 解析
- 如果有 ::: demo 场景,进入自定义解析 ::: demo 流程
- 将 demo 里的 ```vue 代码块字符串化后放到 标签里的 highlight 插槽上。
- 字符串化的过程做了系列代码的高亮、行号等显示处理
- 再把 ```vue 代码块封装到单独的 vue 组件里,组件内部自动命名,然后给挂载到 标签里的 source 插槽上
- 组件就可以用 highlight 插槽来把代码块呈现出来,同时用 source 插槽来引用 loader 生成的子组件,以达到代码块运行的效果
- 将 demo 里的 ```vue 代码块字符串化后放到 标签里的 highlight 插槽上。
require.context 自动注册路由
// 递归遍历当前目录下为 .md 结尾的文件
const files = require.context(".", true, /\.md$/);
files.keys().forEach((filePath) => {
// 省略根据文件路径名生成路由配置信息
// 生成路由配置相关信息,路由直接以组件目录名
const routerConfig = {
title: fileName,
path: `/${pathParts.join("/")}/${fileName}`,
component: files(filePath).default,
};
});
这样就不需要每新增一个组件, 都需要手动去注册路由信息了
注: 脚本可以借助 ChartGPT 完成, 描述好诉求就行
monaco-editor 在线代码编辑器
小结
只需用 md 就能完成组件使用平台的搭建, 而无需再编写额外的 demo 等成本投入, 较低成本换来使用人的直观, 方便, 快捷的使用组件
自定义md-loader来简单高效的维护组件文档的更多相关文章
- SpringBoot整合Swagger2,再也不用维护接口文档了!
前后端分离后,维护接口文档基本上是必不可少的工作.一个理想的状态是设计好后,接口文档发给前端和后端,大伙按照既定的规则各自开发,开发好了对接上了就可以上线了.当然这是一种非常理想的状态,实际开发中却很 ...
- [2013-03-14]使用wiki维护产品文档
word文档作为产品文档的问题: word文档本身的设计是为了打印: word文档的编辑较为繁琐: 作为产品文档的word文档往往长达百页以上,难以维护,且容易分散注意力,不利于查阅: 没有一个简单易 ...
- python全栈开发day48-jqurey自定义动画,jQuery属性操作,jQuery的文档操作,jQuery中的ajax
一.昨日内容回顾 1.jQuery初识 1).使用jQuery而非JS的六大理由 2).jQuery对象和js对象转换 3).jQuery的两大特点 4).jQuery的入口函数三大写法 5).jQu ...
- java简单实现用语音读txt文档
最近比较无聊,随便翻着博客,无意中看到了有的人用VBS读文本内容,也就是读几句中文,emmm,挺有趣的,实现也很简单,都不需要安装什么环境,直接新建txt文件,输入一些简单的vbs读文本的代码,然后将 ...
- 高效查看MySQL帮助文档的方法
在mysql的使用过程中, 可能经常会遇到以下问题: 某个操作语法忘记了, 如何快速查找? 如何快速知道当前版本上某个字段类型的取值范围? 当前版本都支持哪些函数?希望有例子说明.. 当前版本是否支持 ...
- 高效查看MySQL帮助文档的方法 (转)
在mysql的使用过程中, 可能经常会遇到以下问题: 某个操作语法忘记了, 如何快速查找? 如何快速知道当前版本上某个字段类型的取值范围? 当前版本都支持哪些函数?希望有例子说明.. 当前版本是否支持 ...
- RAP, 高效前后端联调框架,接口文档管理工具
RAP通过GUI工具帮助WEB工程师更高效的管理接口文档,同时通过分析接口结构自动生成Mock数据.校验真实接口的正确性,使接口文档成为开发流程中的强依赖.有了结构化的API数据,RAP可以做的更多, ...
- 用python批量生成简单的xml文档
最近生成训练数据时,给一批无效的背景图片生成对应的xml文档,我用python写了一个简单的批量生成xml文档的demo,遇见了意外的小问题,记录一下. 报错问题为:ImportError: No m ...
- 【中文分词】简单高效的MMSeg
最近碰到一个分词匹配需求--给定一个关键词表,作为自定义分词词典,用户query文本分词后,是否有词落入这个自定义词典中?现有的大多数Java系的分词方案基本都支持添加自定义词典,但是却不支持HDFS ...
- SDWebImage ReadMe.md文档简单说明
SDWebImage ReadMe.md 文档 附:SDWebImage框架github下载地址:https://github.com/rs/SDWebImage 注1:该文章简单翻译了SDWebIm ...
随机推荐
- numpy中矩阵的逆,求解,特征值,特征向量
逆:numpy.linalg.inv() # 求矩阵的逆import numpy as npa=np.mat('1 0;0 1')#生成一个矩阵print(type(a))b=np.linalg.in ...
- Solution -「CF 1477A」Nezzar and Board
Description Link. $ n $ distinct integers $ x_1,x_2,\ldots,x_n $ are written on the board. Nezzar ca ...
- heygen模型接口 简单使用 java版
HeyGen - AI Spokesperson Video Creator 官网地址 Create a video (heygen.com) api地址 简介: 公司最近对ai方面业务比较感兴趣了 ...
- C++20起支持的一个小特性
注释掉的为传统的写法,从C++20起支持default关键字修饰的写法,即使是成员变量有多个的时候也支持,减轻了程序员的心智负担.
- 图形学、02 推导证明 | 任意一点经过透视投影后 z 坐标相对于之前有什么变化
齐次坐标知识点: \(\begin{bmatrix} x \\ y \\ z \\ 1 \\\end{bmatrix} \Rightarrow\begin{bmatrix} nx \\ ny \\ n ...
- 文心一言 VS 讯飞星火 VS chatgpt (114)-- 算法导论10.2 7题
七.用go语言,给出一个 O(n)时间的非递归过程,实现对一个含 n个元素的单链表的逆转.要求除存储链表本身所需的空间外,该过程只能使用固定大小的存储空间. 文心一言: 在Go语言中,你可以使用迭代方 ...
- 【BUU刷题日记】--第二周
[BUU刷题日记]--第二周 一.[WUSTCTF2020]朴实无华 1 目录爆破 使用dirsearch扫描发现没有结果,因为如果dirsearch请求过快则会导致超出服务器最大请求,扫描不出本来可 ...
- mapState、mapGetters、mapMutations、mapActions学习
https://next.vuex.vuejs.org/zh/guide/state.html#mapstate-%E8%BE%85%E5%8A%A9%E5%87%BD%E6%95%B0 https: ...
- arm架构docker安装nacos
前言 搞了个hk1box,装了armbian系统,想用这个当服务器调试微服务,需要安装nacos.尝试安装非docker版本的nacos,去github下载arm版本的并且放到linux下面,运行的时 ...
- 【scipy 基础】--插值
插值运算是一种数据处理方法,主要用来填补数据之间的空白或缺失值.因为在实际应用中,数据往往不是完整的,而是存在着空白或缺失值,这些空白或缺失值可能是由于数据采集困难.数据丢失或数据处理错误等原因造成的 ...