解析Markdown文件生成React組件文檔
前言
最近做的項目使用了微前端框架single-spa。
對於這類微前端框架而言,通常有個utility應用,也就是公共應用,裡面是各個子應用之間可以共用的一些公共組件或者方法。
對於一個團隊而言,項目中公共組件和方法的使用難點不在於封裝不在於技術,很多時候在於團隊內部成員是否都能了解這些組件,以避免重複開發,從而提升團隊效率。
如果是團隊比較小,人員比較穩定的項目組可能還好點,對於團隊比較大,人員流動較快的團隊,這些通用組件和方法往往就被人遺忘在角落,很難再得到有效利用。
因為我所在的項目還在開發初期,並且是新入職也想去熟悉一下當前項目中的一些通用組件和方法,所以我自己特意開發了一個文檔應用去解決這個問題。
技術方案選型
對於一個面向Ant Design編程的鹹魚而言,這個文檔應用肯定是往這個方向做。
目標是能做成Ant Design的組件文檔那樣好用,既能很快看清組件的使用效果,也能快速複製示例程式碼。
有了目標之後,很快選定了兩個技術方案
- StoryBook方案
- 自己開發解析markdown文件的文檔應用
StoryBook是市面上一款比較流行的構建UI組件和文檔的庫,功能很強大。
但是這個庫如果要應用到我們項目的團隊存在以下問題:
- 英文文檔,有學習成本
- 引入single-spa的utility應用很麻煩
- 對於構建在utility應用中的組件,需要在StoryBook中再寫一遍,容易不同步
- 對於通用方法可能不支援
尤其是第三點特別存在問題,團隊成員大都是業務開發,有交付壓力,不是在github上為愛發電的開源貢獻者。
他們是否有意願在utility應用中寫了一遍組件程式碼後,又專門跑到這個StoryBook中再寫一道?
如果組件程式碼修改後,StoryBook這邊沒修改,這種不同步很容易導致開發人員明明按照文檔使用組件,但是組件就是報錯,挫敗感很強。
聯繫到現實,我已經預想到如果走這個方案,一個月內這個玩意就會名存實亡,成為形式主義的存在,在三個月後成為大家都不願提起的垃圾了。
所以有了第二個方案的誕生:自己開發解析markdown文件的文檔應用。
這個方案並不是我憑空想像出來的,而是在前公司中就有這麼一個內部應用,組件開發人員自己編寫markdown文件,最終生成組件文檔,並且應用本身可以解析markdown文件中的程式碼部分,從而在組件文檔中生成對應的組件示例。
彼時我只是一個組件開發人員,並不是這個文檔應用的實現人員,所以也不知道其技術原理。
但是前公司這個應用讓我知道了這麼玩行得通,當時作為組件開發人員,接受和使用這個應用異常輕鬆,只要會寫markdown就行了,沒有學習成本。而且使用這種方式,控制權掌握在自己手中,更容易和自己的團隊項目有效結合起來。
基本原理
雖然這個文檔應用是我一個人花了兩天的時間獨立完成,但是花的是工作時間,完成後也是公司內部項目,所以這個文檔應用我沒法開源出來。
不過我可以告訴大家一個主要思路和步驟,想必復現出來也並不困難。
第一步讓我們搞定這個項目的框架。
因為需要引入single-spa的utility應用,所以框架我直接用的是single-spa的基座,並且項目內含一個子應用用於展示文檔。而utility應用直接引入線上開發環境的utility應用,避免團隊成員重複書寫組件程式碼,也解決了文檔和實際應用不同步的問題。
通過這一步,我們解決了StoryBook方案中的痛點2和痛點3。
第二步我們需要載入markdown文件。
這一步肯定是通過webpack的載入器來做處理,這些載入器有的比較強大,可以直接將markdown文件轉換為html。但是我並沒有選擇這種,而是直接用的raw-loader,將我們的markdown文件轉換為字元串載入。
程式碼大致如下,這個比較簡單,就不說了。
module.exports = {
module: {
rules: [
{
test: /\.md$/,
use: 'raw-loader'
}
]
}
}
第三步我們需要解析markdown文件生成文檔,並解析其中的React組件,生成組件示例。
解析markdown文件轉換為html文檔,實際上有個比較強大的庫,叫Showdown。
而我所用到的庫react-showdown則是對Showdown的進一步封裝,可以藉助一個react組件將markdown和包含在markdown文件中的react組件渲染成html。
下面是它的一個官方示例:
import React from 'react';
import MarkdownView from 'react-showdown';
export default function App() {
const markdown = `
# Welcome to React Showdown :+1:
To get started, edit the markdown in \`example/src/App.tsx\`.
| Column 1 | Column 2 |
|----------|----------|
| A1 | B1 |
| A2 | B2 |
`;
return (
<MarkdownView
markdown={markdown}
options={{ tables: true, emoji: true }}
/>
);
};
通過MarkdownView這個組件,可以將一串markdown格式的文本轉化為html。
另外我們注意到它的選項,tables為true,如果不設置這個的話,markdown中的table格式將不會被轉化成表格。第二個emoji為true是支援emoji轉換。
這個時候你可能要問,這只是轉換了一下markdown文件而已,轉換react組件呢?
我們可以看一下下面這個官方示例:
import MarkdownView from 'react-showdown';
function CustomComponent({ name }: { name: string }) {
return <span>Hello {name}!</span>;
}
const markdown = `
# 我是個標題:
<CustomComponent name="world" />`;
<MarkdownView markdown={markdown} components={{ CustomComponent }} />
在markdown文本中可以直接寫上CustomComponent這個自定義的react組件程式碼,然後在MarkdownView的components中傳入CustomComponent即可。
生成的最終html中不僅會有個標題,標題下面還會展示一個叫hello world!的文本,而不是展示<CustomComponent name=”world” />這個字元串。
排疑解難
看完了上面的原理,想必您已經可以實現這樣的一個文檔應用了。
不過在這個過程中您可能還是會遇到一些小麻煩,這裡提前給您支個招。
麻煩1:markdown轉換成html後的程式碼高亮處理。
因為我們做的是一個組件文檔,那麼肯定會涉及到程式碼展示。
markdown文件中的程式碼塊,使用react-showndown轉換後的並沒有做高亮處理。
不過react-showdown是支援Showdown的各種擴展的,其中有個擴展叫showdown-highlight,通過這個擴展可以對程式碼塊做高亮處理。
麻煩2:react-showndown只支援簡單的組件。
雖然react-showndown可以解析react組件程式碼,但是它也只能簡單解析這個組件。如果我們演示的示例比較複雜,涉及到一些函數,還有一些庫的引用,很顯然不能再markdown文件中直接寫。
這裡我建議直接將每個組件的示例寫到一個獨立的js中,這個js導出一個Demo組件,然後我們在markdown文件中直接引用這個demo組件即可。
大致程式碼如下:
import MarkdownView from 'react-showdown';
import ButtonDemo from './ButtonDemo';
const markdown = `
# 按鈕組件
組件描述
## 程式碼示例
<ButtonDemo />
```tsx
這裡貼出以ButtonDemo組件中的程式碼
```
## API
| 屬性 | 說明 |XXX|
|----------|----------|-----|
| title | 按鈕文本 | XXX |
| type | 按鈕類型 | XXX |
`;
<MarkdownView markdown={markdown} components={{ ButtonDemo }} />
通過上面這種方式,不論我們ButtonDemo中的邏輯和功能多麼複雜,展示出來都是沒問題的。
麻煩3:如何將這個文檔應用做到簡單好用。
看了上面的程式碼,可能有人會覺得應該沒問題了。
但是我們得明白,我們這個東西是做給業務開發的人員用的,而不是做給我們自己用的。
我業務開發人員為什麼要知道你這些什麼 react-showdown 的程式碼?
我業務開發人員還要學習你的這些鬼東西?
不是每個人都想著學這些亂七八糟的技術好嗎?
我每天就只想在6點下班,就算你5分鐘內給我講明白了,你這個文檔應用我用不用還兩說。
你要是5分鐘之內還講不明白怎麼用,那你休想我在這上面給一個公共組件寫文檔。
我們面對的基本就是這麼一個場景,我們做這個應用是為了解決項目中實際面臨的問題,是面向業務開發人員編程,而不是面向領導和KPI編程。
所以我們需要做到簡單好用,將所有涉及到react-showdown這玩意的部分全部不被業務開發人員感知。
想像一下,寫一個組件的文檔,縮減到最少,就是一個markdown文件,和一個demo.js。
那麼我們就只讓業務開發人員去寫這兩個東西就行,把他們的工作量減少到最小。
就給他們兩個文件夾,一個文件夾叫doc,裡面放markdown文件,一個文件夾叫demo,裡面放各個demo。
再用一個字典配置dict.js,去做個基本的配置。
如果現在有個Easy組件要寫文檔,那麼我們的dict.js內容可能就是下面這樣:
const dict=['Easy','Hard','XXX']
export defalt dict
只需要加個字元串Easy即可。
然後你可以在那麼doc文件夾下加個markdown文件叫EasyMD.md,demo文件夾下加個文件叫EasyDemo.tsx。
之後的所有步驟全部由我們的文檔應用解析dict.js後自動完成,無需用戶操心。
通過這樣的一種約定,我們可以將業務開發人員的工作量減到最小,把他們寫組件文檔的門檻降到最低。
具體程式碼實現就略過了,實現的關鍵詞叫:import()函數,其他的不用多說了。
總結
雖然說這個文檔應用是受前公司啟發,而且因為開發時間就兩天,所以比較簡陋,但是至少我做到了比前公司的內部應用更簡單方便,完全沒有學習成本。
好了,自吹一波就得了,本篇部落格到此結束。
如有疏漏之處,還請不吝賜教。