自定义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 是我前司一同事自己開發的,這是他的源碼倉庫和技術實現細節文檔:
- https://github.com/luchx/md-enhance-vue
- 在 Markdown 中 使用 Vue
原理細節和源碼可以移步到相關鏈接查看,這里簡單概述下 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 代碼塊字符串化后放到
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 在線代碼編輯器
Vue 實現在線代碼編輯和預覽
小結
只需用 md 就能完成組件使用平臺的搭建, 而無需再編寫額外的 demo 等成本投入, 較低成本換來使用人的直觀, 方便, 快捷的使用組件
總結
以上是生活随笔為你收集整理的自定义md-loader来简单高效的维护组件文档的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【OpenCV】 OpenCV 源码编译
- 下一篇: VSCode 中优雅地编写 Markdo