Element 文檔中的 Markdown 解析
Element 的文檔站是講Markdown解析成vue組件在頁面中渲染出來,轉換過程如下圖所示:
紅框部分勢必要對 Markdown 進行特殊的訂製,訂製過的 Markdown 像下面這樣。
:::demo 要使用 Radio 組件,只需要設置`v-model`綁定變數,選中意味著變數的值為相應 Radio `label`屬性的值,`label`可以是`String`、`Number`或`Boolean`。
```html
<template>
<el-radio v-model="radio" label="1">備選項</el-radio>
<el-radio v-model="radio" label="2">備選項</el-radio>
</template>
<script>
export default {
data () {
return {
radio: '1'
};
}
}
</script>
\`\`\`
:::
需要解析成對應的頁面如下圖:
通過 :::demo 作為頁面中組件實例的標識,這個轉換過程在md-loader中處理。具體element文檔站如何實現解析功能的,看看源碼build文件下的webpack.demo.js配置md的解析器。
webpack配置
把Markdown解析成vue組件就在webpack配置md的解析loader:
{
test: /\.md$/,
use: [
{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{
loader: path.resolve(__dirname, './md-loader/index.js')
}
]
},
從配置文件中可以看出,Markdown 先經由 md-loader 處理,然後再交由 vue-loader 處理。經過這兩個 loader 的處理後輸出JavaScript在頁面中渲染出來。
md-loader
源碼中md-loader目錄如下:
├─md-loader
| ├─config.js
| ├─containers.js
| ├─fence.js
| ├─index.js
| ├─util.js
index.js:
const {
stripScript,
stripTemplate,
genInlineComponentText
} = require('./util');
const md = require('./config');
module.exports = function(source) {
const content = md.render(source);
const startTag = '<!--element-demo:';
const startTagLen = startTag.length;
const endTag = ':element-demo-->';
const endTagLen = endTag.length;
...
md.render(source)
這句程式碼是在markdown-it
插件(將markdown轉換為html插件)用法很相似,根據const md = require('./config');
可以猜測markdown轉換為html的程式碼在config中。
config.js:
const Config = require('markdown-it-chain');
const anchorPlugin = require('markdown-it-anchor');
const slugify = require('transliteration').slugify;
const containers = require('./containers');
const overWriteFenceRule = require('./fence');
const config = new Config();
config
.options.html(true).end()
.plugin('anchor').use(anchorPlugin, [
{
level: 2,
slugify: slugify,
permalink: true,
permalinkBefore: true
}
]).end()
.plugin('containers').use(containers).end();
const md = config.toMd();
overWriteFenceRule(md);
module.exports = md;
程式碼中用到很多插件,我們先百度下這幾個插件的作用。
markdown-it-chain:npm上markdown-it-chain包的描述是這樣的:
In order to ensure the consistency of the chained API world, webpack-it-chain is developed directly on the basis of webpack-chain and ensures that the usage is completely consistent.Here are some things worth reading that come from webpack-chain:ChainedMapConfig plugins
因為英語不好,我用Google翻譯:
為了確保鏈式API世界的一致性,直接在webpack-chain的基礎上開發了webpack-it-chain,並確保用法是完全一致的。
確保鏈式API世界的一致性
這句基本沒看懂,可能作者老哥也和我一樣英語不好,但我們可以知道這個插件是在webpack-it-chain
的基礎做的功能完善優化。通過給的markdown-it-chain的例子我們知道 config.js程式碼主要就是在聲明使用markdown-it-chain的。markdown-it-chain的例子程式碼如下:
// Require the markdown-it-chain module. This module exports a single
// constructor function for creating a configuration API.
const Config = require('markdown-it-chain')
// Instantiate the configuration with a new API
const config = new Config()
// Make configuration changes using the chain API.
// Every API call tracks a change to the stored configuration.
config
// Interact with 'options' in new MarkdownIt
// Ref: //markdown-it.github.io/markdown-it/#MarkdownIt.new
.options
.html(true) // equal to .set('html', true)
.linkify(true)
.end()
// Interact with 'plugins'
.plugin('toc')
// The first parameter is the plugin module, which may be a function
// while the second parameter is an array of parameters accepted by the plugin.
.use(require('markdown-it-table-of-contents'), [{
includeLevel: [2, 3]
}])
// Move up one level, like .end() in jQuery.
.end()
.plugin('anchor')
.use(require('markdown-it-anchor'), [{
permalink: true,
permalinkBefore: true,
permalinkSymbol: '$'
}])
// Apply this plugin before toc.
.before('toc')
// Create a markdown-it instance using the above configuration
const md = config.toMd()
md.render('[[TOC]] \n # h1 \n ## h2 \n ## h3 ')
要知道markdown-it-chain的到底是做什麼的,我去查了下webpack-chain插件
Use a chaining API to generate and simplify the modification of webpack version 2-4 configurations.
鏈式API用於創建和修改webpack配置
就是提供一些鏈式函數的調用方法來修改和創建webpack
配置,例子如下:
const Config = require('webpack-chain');
const config = new Config();
config
.amd(amd)
.bail(bail)
.cache(cache)
.devtool(devtool)
.context(context)
.externals(externals)
.loader(loader)
.name(name)
.mode(mode)
.parallelism(parallelism)
.profile(profile)
.recordsPath(recordsPath)
.recordsInputPath(recordsInputPath)
.recordsOutputPath(recordsOutputPath)
.stats(stats)
.target(target)
.watch(watch)
.watchOptions(watchOptions)
至此第一個插件markdown-it-chain我們知道了他的用處:用鏈式調用的方法來創建和修改markdown-it
配置,而其中plugin
是給markdown-it
配置一些插件。config.js程式碼中
.plugin('anchor').use(anchorPlugin, [
{
level: 2,
slugify: slugify,
permalink: true,
permalinkBefore: true
}
]).end()
.plugin('containers').use(containers).end();
就是給markdown-it
添加markdown-it-anchor
和containers.js
插件。
那麼這裡拋出一個問題,為什麼使用markdown-it-chain
,它帶來的好處是什麼呢?
npm上 webpack-chain的文檔是這麼說的:
webpack’s core configuration is based on creating and modifying a potentially unwieldy JavaScript object. While this is OK for configurations on individual projects, trying to share these objects across projects and make subsequent modifications gets messy, as you need to have a deep understanding of the underlying object structure to make those changes.
webpack的核心配置基於創建和修改可能難以使用的JavaScript對象。 儘管這對於單個項目上的配置是可以的,但是嘗試在項目之間共享這些對象並進行後續修改會很麻煩,因為您需要對基礎對象結構有深刻的了解才能進行這些更改。
大概意思理解了,但因為沒有經常操作webpack的配置所以對嘗試在項目之間共享這些對象並進行後續修改會很麻煩
這個點get不到。後續去找資料麻煩的點具體是指的什麼,大家也可以一起討論下。覺得寫的可以點個贊。下篇繼續!