這是我參與8月更文挑戰的第2天,活動詳情查看:8月更文挑戰html
Vuepress
Element
的源碼找出了我認爲的一種最優解,藉此機會分享一下先把最後的成品發出來給你們看看vue
在上篇中分析概括了一下具體的步驟(以下),接下來咱們就來具體的實現一下webpack
demo
而且統同樣式Markdown
自定義容器來編寫demo
代碼md
使用自定義容器來實現上文效果markdown
渲染方法使咱們輸入的代碼塊能夠輸出內容爲符合Vue Template
語法的代碼塊vue
代碼後交由Vuepress
的vue-loader
處理編譯爲文檔docs
├─ .vuepress
│ ├─ components
│ │ └─ demoBlock.vue
│ ├─ config.js
│ └─ enhanceApp.js
└─ component
└─ basic
└─ button.md
複製代碼
Vuepress
能夠自動識別components
裏面的組件並註冊因此咱們在裏面建一個通用組件demoBlock
用於展現demo
Element
的通用組件後觀察到這個組件主要由三部分組成:組件示例
、描述
、組件代碼塊
/* demoBlock.vue */
<template>
<div class="block">
<div class="demo-content">
<!-- 插入組件 -->
<slot name="demo"></slot>
</div>
<div class="meta" ref="meta">
<div class="description">
<!-- 插入描述信息 -->
<slot name="description"></slot>
</div>
<div class="code-content">
<!-- 插入代碼塊 -->
<slot name="source"></slot>
</div>
</div>
</div>
</template>
複製代碼
組件示例
、描述
、組件代碼塊
,這樣咱們就能夠經過在md
轉vue
的時候根據特別的插槽來組裝咱們的組件示例對於自定義組件咱們可使用markdown-it-container 參考官網構建git
/* containers.js */
const mdContainer = require('markdown-it-container');
module.exports =md => {
//將markdown-it-container插件加載到當前的解析器實例中
md.use(mdContainer, 'demo', {
validate(params) {
//函數在開始標記後驗證尾部,成功時返回true
return params.trim().match(/^demo\s*(.*)$/);
},
render(tokens, idx) {
//渲染器函數
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
if (tokens[idx].nesting === 1) {
const description = m && m.length > 1 ? m[1] : '';
// opening tag
return `<demo-block>
<div slot="demo">組件demo</div>
<div slot="description">${description}</div>
<div slot="source">代碼塊</div>`
} else {
// closing tag
return `</demo-block>`;
}
}
});
}
複製代碼
markdown-it-container
支持兩個參數,第一個是自定義容器的名字,第二個是一些選項github
true
成功時應返回:
),在分隔符中使用的字符在render
方法中也有兩個參數web
token
們的列表token
的索引值得一提的是token的兩個屬性npm
1
表示打開 0
表示自動關閉 -1
表示正在關閉在選項中的render
能夠對自定義的容器作渲染處理,像上面我就讓識別到demo
的自定義容器渲染成這個格式,那麼咱們在md
文件輸入自定義容器時就會找到對應的組件進行渲染api
建立完自定義容器後咱們要組裝到Vuepress
的配置中,Vuepress
自帶了chainMarkdown來修改內部的markdown
配置,具體的配置操做能夠參考配置插件markdown
/* config.js */
module.exports = {
title: 'Zylw-Ui',
description: '開始你的組件化之旅吧~',
...
plugins: [
[
require('./md-loader')
]
]
...
}
複製代碼
/* index.js */
const demoBlockContainers = require('./common/containers')
module.exports = () => {
return {
chainMarkdown(config) {
//修改內部的 markdown 配置
config // 增長額外的插件markdownContainers
.plugin('markdownContainers')
.use(demoBlockContainers)
.end();
}
}
}
複製代碼
嘗試在md
文件中使用後效果就出來了編輯器
此時的結構是這樣的
docs
├─ .vuepress
│ ├─ components
│ │ └─ demoBlock.vue
│ ├─ config.js
│ ├─ enhanceApp.js
│ └─ md-loader
│ ├─ common
│ │ └─ containers.js
│ └─ index.js
└─ component
└─ basic
└─ button.md
複製代碼
大致效果架子出來了以後,咱們就要考慮如何將自定義容器裏的內容分別輸出到組件位置
和代碼塊位置
,一個典型的單文件組件包括三塊:template
script
與 style
,那麼接下的重點就是如何拼湊出template
與script
的內容
咱們能夠參考element
的作法,因爲代碼太長,先放上Element的源碼能夠一塊兒食用 能夠看到Element在渲染的時候加入了一個佔位符來接受咱們的代碼塊,再經過編譯的時候對這個註釋塊進行處理就能夠分別轉化到template
script
與 style
這時咱們就要改寫一下咱們的結構(此代碼靈感來自Demo Container)
/* containers.js */
const mdContainer = require('markdown-it-container');
module.exports =md => {
//將markdown-it-container插件加載到當前的解析器實例中
md.use(mdContainer, 'demo', {
validate(params) {
//函數在開始標記後驗證尾部,成功時返回true
return params.trim().match(/^demo\s*(.*)$/);
},
render(tokens, idx) {
//渲染器函數
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
if (tokens[idx].nesting === 1) {
const description = m && m.length > 1 ? m[1] : '';
const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : '';
return `<demo-block>
<template slot="demo"><!--pre-render-demo:${content}:pre-render-demo--></template>
${description ? `<div slot="description">${md.render(description).html}</div>` : ''}
<template slot="source">
`;
}
return `</template></demo-block>`;
}
})
}
複製代碼
containers.js
截取類型爲fence
的代碼塊放到佔位符中render.js
對佔位符的內容進行處理 具體代碼Vuepress
的 extendMarkdown API 繼續拓展了其內部的markdown
對象,修改內部用於渲染markdown
文件的 markdown-it實例的配置/* index.js */
const renderDemoBlock = require('./common/render')
module.exports = () => {
return {
...
extendMarkdown: md => {
//修改內部用於渲染 markdown 文件的 markdown-it實例的配置
const id = setInterval(() => {
const render = md.render;
if (typeof render.call(md, '') === 'object') {
md.render = (...args) => {
let result = render.call(md, ...args);
//分別提取三大塊進行拼接
const { template, script, style } = renderDemoBlock(result.html);
result.html = template;
result.dataBlockString = `${script}\n${style}\n${result.dataBlockString}`;
return result;
}
clearInterval(id);
}
}, 10);
}
}
}
複製代碼
demo
就大功告成啦!!!對比以前冗餘寫法是否是方便特別多呢,接下來只須要在demoBlock.vue
更改屬於本身的樣式就能夠啦docs
├─ .vuepress
│ ├─ components
│ │ └─ demoBlock.vue
│ ├─ config.js
│ ├─ enhanceApp.js
│ └─ md-loader
│ ├─ common
│ │ ├─ render.js
│ │ ├─ util.js
│ │ └─ containers.js
│ └─ index.js
└─ component
└─ basic
└─ button.md
複製代碼
markdown-it-container
自定義容器的方法結合vue-template-compiler
將代碼片斷轉換成組件,不一樣的文檔編輯器可能有不一樣的辦法但原理都是相同的要麼經過自身支持的插件進行配置要麼經過Webpack
進行配置markdown
轉html
的一些原理,因此仍是那句話多看源碼真的頗有用