这篇文章先简单介绍下流程和需要关注的API,以及简单的实现一个自定义的vite plugin。后续跟上更加详细的实现。
懵逼的开始
之前学习Naive-UI的源码的时候,发现一些很不能理解的东西,譬如:
这是它文档的路由信息
export const zhComponentRoutes = [
// 省略其他...
{
path: 'checkbox',
component: () => import('../../src/checkbox/demos/zhCN/index.demo-entry.md')
},
{
path: 'button',
component: () => import('../../src/button/demos/zhCN/index.demo-entry.md')
},
// 省略其他...
]
而这个md文件又是这样的:
而描述文件里的```demo ``` 中的几个vue文件又是这样的:
啥?啥?啥?这都是些啥?vue-router
可以加载md文件了吗?md文件里的demo
又是什么语言代码块?demo.vue
里的<markdown></markdown>
标签又是啥?这玩意里的md语法格式是咋解析的?
小脑袋里满满的问号,然后就开始了查看他的代码之路。终于有了一定程度的理解。
简述工作流程
这整个流程是怎么实现的呢?
看过了他们的代码后才知道,他们自己实现的vite插件来解析md文档,最终输出为合规的vue文件。(这种设计让弟弟叹为观止,毕竟我才工作两年半,直接整开了我的思维)
graph TD
请求传入模块 --> vite插件 --> md文件 --> marked解析语法 --> 处理demo代码块 --> 生成template和script文本 --> 生成锚点数据
marked解析语法 --> 自定义Render
vite插件 --> demo.vue和demo.md
demo.vue和demo.md --> demo.vue --> 处理演示文件常规标签和markdown标签 --> 替换模板vue文件中的占位符生成最终vue文件
demo.vue和demo.md --> demo.md
生成锚点数据 --> vue
替换模板vue文件中的占位符生成最终vue文件 --> vue
前置知识
Vite plugin 具体文档请看这里
重点关注其中的transform
钩子,该钩子是通用钩子,是在传入每个模块请求时被调用,可以转译单个模块,可以在这里生成AST
,也可以给正在转译的模块做一些其他处理。
看到这里,你是不是脑袋里灵光一闪?我们可以在这里对md文本先这样,再这样,然后继续这样,不就可以实现了吗?
marked md文件解析工具 具体文档请看这里
marked
是一款将md语法解析处理的插件,功能十分强大,支持自定义render格式、配合hightlight.js
,实现语法高亮等等功能。在Naive-UI
的插件里,就进行了自定义,解析成了它自有的组件格式,完美实现自给自足。本文实现的功能里,需要用到它的lexer、parser
和自定义它的渲染器
插件的简单实现
这里先带着大家简单的实现一个插件,详细的会在后续文章中补充
Project init
新建一个目录(大家随意)useless
,然后,我们进行项目初始化:
pnpm create vite useless --template vue
cd useless && pnpm dev
ok~,启动成功。
vite-plugin-inspect vite插件检查工具这里看文档
我们先将对我们有帮助的东西安装好,事半功倍。我们来安装vite插件的检查插件,这个插件可以让我们更好的看到插件的中间状态
npm i -D vite-plugin-inspect
接着改造我们的vite.config.js
,将插件配置进去:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import Inspect from "vite-plugin-inspect";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), Inspect()],
});
接着我们重新启动项目,执行命令pnpm dev
,就可以在控制台看到我们的检查插件的地址了
VITE v4.0.4 ready in 325 ms
➜ Local: http://localhost:5174/
➜ Network: use --host to expose
➜ Inspect: http:/localhost:5174/__inspect/
➜ press h to show help
我们进入检查插件的地址查看,就能看到一个这样的界面:
看起来是不是有点看不懂?没关系,vite自带的plugin-vue
我们暂时不用关注,我们先学着怎么自己去处理,好了,开始下一步。
自定义一个plugin
我们新建一个plugin
文件夹,专门来存放我们的文件
新建一个index.js
文件,代表起始文件,也方便阅读,我们将vite的plugins
单独抽取出来,代码如下:
import vue from "@vitejs/plugin-vue";
import Inspect from "vite-plugin-inspect";
const createMyPlugin = () => {
return [vue(), Inspect()];
};
export default createMyPlugin;
同时将vite.config.js
文件中的plugins
改成调用createMyPlugin
的方式:
import { defineConfig } from "vite";
import createMyPlugin from "./plugins";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [createMyPlugin()],
});
紧接着,我们来实现一个简单的功能,给传入的vue文件增加一个hello world
文本,完整代码如下:
import vue from "@vitejs/plugin-vue";
import Inspect from "vite-plugin-inspect";
const fileRegex = /\.vue$/;
const createMyPlugin = () => {
const myPlugin = {
name: "add-hello-world",
transform(code, id) {
if (fileRegex.test(id)) {
return code.replace(
"<template>",
`<template>
<h1>Hello World</h1>`
);
}
},
};
return [myPlugin, vue(), Inspect()];
};
export default createMyPlugin;
这时候我们来看下页面和插件的检查工具,会是这个样子的:
可以清晰的在检查工具中看到,文件经过我们定义的add-hello-word
插件,对app.vue
和HelloWorld.vue
进行了改造,匹配到<template>
标签,并插入了<h1>Hello World</h1>
,所以我们在页面里就看到了两个大大的Hello World
。至此,我们一个简单的插件就完成了,不过仅仅是最简单的改动,实际上要实现Naive-UI
那种结构,还需要很大的改动量,我会一步步来将它们的实现方式来解释清楚。
仓库地址
文档相关的代码,已经放到了tiny-dust/useless (github.com)上,后续我跟根据文章打上对应的tags和分支,方便大家更好的对比查看
下一篇:如何通过自定义的Vite Plugin实现解析md文件
结语
想到写这个文章,既是为了分享,也是为了学习。有些不对的地方,还请大家指正。我的思维受限于自己的认知,所以很难去想到一些奇奇怪怪(对于我来说奇奇怪怪)的操作。过去的一年里,一有时间就刷卡颂、若川、闲强等等大佬的文章,总是震惊于他们怎么理解的这么透彻。随着自己跟着尝试去查看源码,一步步的搜索答案,才发现,原来自己之前的世界那么的小。又想起自己很喜欢的一句话。 我本凡尘微末,却也心向天空
继续加油~