自定义一个Vite Plugin
1.初始化项目

在项目里写一个demo.md文件:

然后在App.vue文件里引入这个md文件
<script setup></script>
<template>
<div>
<p>下面是引入的md内容:</p>
<g-markdown file="./demo.md"></g-markdown>
</div>
</template>
<style scoped></style>
然后【npm run dev】项目启动看看。

【g-markdown】没有解析,这是正常的。环境已经都创建好了,下面我们开始着手写plugin了,解析这个标签,读取md文件。
2.初始化插件
2.1 写一个插件空壳子
//plugins/markdown/index.js
export default function markdownPlugin() {
return {
//插件名称
name: "vite:markdownPlugin",
// 强制插件排序
// pre 在vite核心插件之前调用它
// post 在vite构建插件之后调用它
// 默认 在vite核心插件之后调用它
enforce: "pre",
// 插件功能实现
transform(code, id, opt) {},
};
}
在vite配置文件引入:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import markdownPlugin from "./plugins/markdown/index.js";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), markdownPlugin()],
});
2.2 transform中的参数
- code:文件的源码字符串
- id:文件的路径path
- opt:{ssr:boolean}


我们主要用到的就是code和id,也就是源码和文件,我们做一个粗糙的版本,只限制vue文件,判断id是以【.vue】结尾的,然后我就处理。

然后【npm run dev】项目启动:

这个方法返回的啥源码字符串,最后页面渲染的就是以这个为newSourceCode然后进行vue解析编译渲染。
那我们的思路是:
在code中匹配到【<g-markdown file="./demo.md"></g-markdown>】,并获取这个file,然后借助node的path,获取md文件的字符串,再借助一个【markdown-it】这个第三方插件,将md转为html,最后来个字符串替换,然后return就好啦!
2.3 完善transform实现逻辑
2.3.1 匹配标签获取md中字符串
匹配到 【g-markdown】这个标签,并读取到它的file属性,然后根据path、fs,读取到md文件的字符串:
transform(code, id, opt) {
if (id.indexOf(".vue") != -1) {
let markdownExp = /<g-markdown[^]*?<\/g-markdown>/g;
let markdownMathList = code.match(markdownExp);
console.log(markdownMathList);
}
},
控制台打印如下:

import path from "path";
import fs from "fs";
export default function markdownPlugin() {
return {
//插件名称
name: "vite:markdownPlugin",
// 强制插件排序
// pre 在vite核心插件之前调用它
// post 在vite构建插件之后调用它
// 默认 在vite核心插件之后调用它
enforce: "pre",
// 插件功能实现
transform(code, id, opt) {
if (id.indexOf(".vue") != -1) {
let markdownExp = /<g-markdown[^]*?<\/g-markdown>/g;
let markdownMathList = code.match(markdownExp);
console.log(markdownMathList);
const filePathExp = /(?<=file=("|')).*(?=('|"))/;
markdownMathList.forEach((markdown) => {
let mdPath = markdown.match(filePathExp)[0];
console.log(mdPath);
const markdwonPath = path.resolve(path.dirname(id), mdPath);
const mdText = fs.readFileSync(markdwonPath, "utf-8");
console.log(markdwonPath);
console.log(mdText);
});
}
},
};
}
控制台打印如下:

2.3.2 将md字符串转为html字符串
上面我们可以看到,我们已经获得了md字符串,但是浏览器是看不懂md的专属语法,所以需要借助第三方工具,编译一下。
npm install markdown-it --save

2.3.3 字符串替换
将获取到的html,替换掉我们写的g-markdown标签。


3.配置热重载
上面渲染ok了,但发现一个问题,md文件更改了,浏览器展示的内容没有更新,每次都需要重新启动。
我们需要在插件的handleHotUpdate钩子函数中,对我们的md类型文件进行监听,再将引用了该md文件的vue文件进行热重载。
import path from "path";
import fs from "fs";
import MarkdownIt from "markdown-it";
const mdRelationMap = {};
export default function markdownPlugin() {
return {
//插件名称
name: "vite:markdownPlugin",
// 强制插件排序
// pre 在vite核心插件之前调用它
// post 在vite构建插件之后调用它
// 默认 在vite核心插件之后调用它
enforce: "pre",
/* 配置热重载 */
handleHotUpdate(ctx) {
const { file, server, modules } = ctx;
if (path.extname(file) !== ".md") return;
const relationIds = mdRelationMap[path.resolve(file)];
const relationModules = [];
relationIds.forEach((relationId) => {
const relationModule = [
...server.moduleGraph.getModulesByFile(relationId),
][0];
server.ws.send({
type: "update",
updates: [
{
type: "js-update",
path: relationModule.file,
acceptedPath: relationModule.file,
timestamp: new Date().getTime(),
},
],
});
relationModules.push(relationModule);
});
return [...modules, ...relationModules];
},
// 插件功能实现
transform(code, id, opt) {
if (id.indexOf(".vue") != -1) {
let markdownExp = /<g-markdown[^]*?<\/g-markdown>/g;
let markdownMathList = code.match(markdownExp);
const filePathExp = /(?<=file=("|')).*(?=('|"))/;
markdownMathList.forEach((markdown) => {
let mdPath = markdown.match(filePathExp)[0];
const markdwonPath = path.resolve(path.dirname(id), mdPath);
const mdText = fs.readFileSync(markdwonPath, "utf-8");
const md = new MarkdownIt();
const htmlText = md.render(mdText);
code = code.replace(markdown, htmlText);
if (!mdRelationMap[markdwonPath]) {
mdRelationMap[markdwonPath] = [];
}
mdRelationMap[markdwonPath].push(id);
});
return code;
}
},
};
}
在markdown文件中加一条信息:

浏览器上内容会自动变化:


8400

被折叠的 条评论
为什么被折叠?



