vue元件庫用markdown生成文件
阿新 • • 發佈:2020-07-22
### 前言:
開發 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
```js
{
test: /\.md$/,
use:[
{loader: 'vue-loader'},
{ loader: path.resolve(__dirname,'./markdown-loader/index.js') }
]
},
```
#### 2.markdown-loader 的入口
```js
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,由自己處理這些程式碼,最終返回想要的內容。
````markdown
::: demo
`html `
:::
````
上面就是外掛的用法,demo 由自己定義,初始注入的程式碼如下:
```js
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 `${content}`;
}
return " ";
},
});
```
render 方法仿照的是 npm 包裡的例子。其中的 tokens 是 AST 節點,可以從[這個網址](http://markdown-it.github.io/)看到 markdown-it 解析的 AST,對照著做判斷。
根據自己的理解,因為 html 是有起始標籤和結束標籤,markdown-it 的 render 也是成對的,也就是在標記的起始和結束都會呼叫 render 方法,所以在 demo 起始的時候返回了一個起始`` (demo-block是個全域性定義的 vue 元件),這裡的content後來變成了vue的元件。
繼續處理 demo 標識內部\`\`\` 程式碼標識,程式碼標識在 markdown-it 中有自己的 rules (`rules.fence`)來處理,結果就是高亮顯示。我們的目標不僅僅如此,還需要讓這部分程式碼有一個顯示隱藏的效果,所以
需要加一個vue的slot標識。
```js
// 先儲存下來
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 `${defaultRender(tokens, idx, options, env, self)} `;
}
return `${defaultRender(
tokens,
idx,
options,
env,
self
)}`;
};
```
需要注意highlight不會把程式碼中的{{tab}} 這種插值轉譯,如果直接給vue渲染會報錯,所以需要新增一個v-pre的指令,需要包裝原始的rules.fence:
```
const ensureVPre = function (markdown) {
if (markdown && markdown.renderer && markdown.renderer.rules) {
const rules = ['code_inline', 'code_block', 'fence']
const rendererRules = markdown.renderer.rules
rules.forEach(function (rule) {
if (typeof rendererRules[rule] === 'function') {
const saved = rendererRules[rule]
rendererRules[rule] = function () {
return saved.apply(this, arguments).replace(/(
```
#### 組裝成 vue 模板
這個程式碼和 vue 的元件的程式碼不一致,是無法解析的,需要修正一下。
另外,一篇文件中會有多個 demo 即多個 export default,解決方案就是把各個 demo 提取成元件,註冊當前文件這個 vue 元件中,把 demo 的部分替換元件的名字。
第一部分:組裝當前文件為 vue 元件 ,同時掛載提取出來 demo 元件[https://github.com/blank-x/kv/blob/master/build/markdown-loader/index.js#L15](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)](https://github.com/blank-x/kv/blob/master/build/markdown-loader/index.js#L57)
```
var renderVueTemplate = function (content) {
......
}
```
結果類似於如下:
```html
.......
```
元件 kv-demo0 和 kv-demo1 在 components 中定義;
在 demo 內部的 scss 會被提出來,放到了外層 vue 元件中,如果需要修改樣式,可以參考如下寫法:
```css
.demo-tag .kv-demo1{
//
}
.demo-tag .kv-demo0{
//
}
tag // md的名字
demo0 // 頁面內第幾個demo
```
#### 未解決的問題
每一個 demo 中 script 標籤和 export 之間的程式碼被丟棄。如果需要引入其他檔案,可以在 data 中通過 require 引入;
#### 最後
本程式碼僅為練手使用,未在實際開發中使用,如有不正之處望指正。
地址[https://github.com/blank-x/kv](https://github.com/blank-x/kv)
......
.......