本文适合对webpack有一丁点了解的同学,因为会涉及到loader的编写。不过不用害怕,因为webpack的loader并不复杂,我们只需要了解一点基础即可。传送门 [编写一个loader](https://webpack.docschina.org/contribute/writing-a-loader/)
阅读官方文档后可以发现,其实我们只需要改变内容的输出即可,在这里要注意一下,loader的加载顺序是从后往前,也就是说,我们要想把md文件编译为vue组件,需要先使用markdown的解析器将md文件编译为html文件,再由vue-loader渲染成vue组件(其中需要注意一下,仓库中是将script标签放在前面,如果有时间可以写正则判断哪些script是属于vue组件的script内容)。大概的流程如图所示
流程图中 md2vue-loader 就是我们写逻辑代码的地方,比如可以自定义md语法,改变输出交给vue-loader,这样我们就完成了md转换成vue组件。接下来,我们详细的介绍 md2vue-loader。
首先我们要明确需求,如何将md文件转换为vue组件。如果还不知道md文件,请移步https://zh.wikipedia.org/wiki/Markdown。
还要明确一点就是,vue组件需要什么内容。请移步https://vue-loader.vuejs.org/zh/spec.html。
那么,我们负责将md文件转换为template
、script
、style
(自定义块不考虑),其中 style
可以从外部导入,md2vue-loader不负责这部分内容 。那我们如何提取其中html
内容和javascript
内容。
我们可以使用社区提供的markdown-it
,仓库地址 https://github.com/markdown-it/markdown-it,markdown-it
提供了render
方法 ,代码如下
// md2vue-loader
module.exports = function (source) {
var MarkdownIt = require('markdown-it'),
md = new MarkdownIt();
var result = md.render(source);
return `<template><div>${result}</div></template>`;
}
到这里,我们已经可以将md文件转换为html文件并输出了。什么,不信?赶快开一个仓库去试一试,或者clone我的仓库试一试https://github.com/Linkontoask/md2vue-loader。
既然得到html内容了是不是大功告成了?不,当然不,之前我们说过vue组件不仅仅只有template
,还有更重要的javascript
部分。我们这里使用一个很简单的方法提取html文件中 ,就是将script
标签放在文件开头,然后再用indexOf
方法判断,再使用 slice
方法切割字符串、拼接后输出。具体代码如下:
module.exports = function (source) {
var MarkdownIt = require('markdown-it'),
md = new MarkdownIt();
const content = md.render(source);
let start = 0, pageScript = '', output = '';
if (content.indexOf('<script>') === 0) {
start = content.indexOf('</script>') + '</script>'.length;
pageScript = content.slice(0, start);
}
output = content.slice(start);
return `
<template>
<section class="markdown-body">
${output}
</section>
</template>
${pageScript}
`;
};
在这里,md2vue-loader
基本已经完成,还有一些功能,比如标题的ID、永久链接、锚点的类名等等。
所有代码请移步到 https://github.com/Linkontoask/md2vue-loader
这里贴一个截止到2019-12-17代码
// index.js
const { getOptions } = require('loader-utils');
const validateOptions = require('schema-utils');
const MarkdownIt = require('markdown-it');
const Hljs = require('highlight.js');
const slugify = require('transliteration').slugify;
const schema = {
type: 'object',
properties: {
html: {
type: "boolean"
},
permalink: {
type: "boolean"
}
}
};
function mdOption (options = {}) {
return new MarkdownIt({
html: options.html,
highlight: function (str, lang) {
if (lang && Hljs.getLanguage(lang)) {
try {
return Hljs.highlight(lang, str, true).value;
} catch (e) {}
}
return str;
}
}).use(require('markdown-it-anchor'), {
slugify: slugify,
permalink: options.permalink
});
}
/**
* @param source
* @returns {string}
*/
module.exports = function (source) {
const options = getOptions(this);
validateOptions(schema, options);
const md = mdOption(options);
const content = md.render(source);
let start = 0, pageScript = '', output = '';
if (content.indexOf('<script>') === 0) {
start = content.indexOf('</script>') + '</script>'.length;
pageScript = content.slice(0, start);
}
output = content.slice(start);
return `
<template>
<section class="markdown-body">
${output}
</section>
</template>
${pageScript}
`;
};
配置参数参考源码,可自行更改。
// webpack.config.js
module: {
rules: [
{
test: /.md$/,
use: [
'vue-loader',
{
loader: path.resolve(__dirname, '../index.js'),
options: {
html: true // 是否渲染原生标签,如果要使用script标签务必设置为true
}
}
],
}
]
}
md2vue-loader参考[Element](https://github.com/ElemeFE/element)实现思路,还有值得优化的部分。比如:
https://github.com/ElemeFE/element/blob/6ec5f8e900ff698cf30e9479d692784af836a108/build/md-loader/index.js#L53github.com转载请注明出处,谢谢。
参考链接
markdown-it | markdown-it 中文文档markdown-it.docschina.org https://github.com/valeriangalliat/markdown-it-anchorgithub.com API — v-easy-componentslinkorg.club