从Naive UI项目上学习Vite Plugin的使用

353 阅读5分钟

这篇文章先简单介绍下流程和需要关注的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文件又是这样的:

image.png

而描述文件里的```demo ``` 中的几个vue文件又是这样的:

image.png

71D51.gif

啥?啥?啥?这都是些啥?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文本先这样,再这样,然后继续这样,不就可以实现了吗? image.png

marked md文件解析工具 具体文档请看这里

marked是一款将md语法解析处理的插件,功能十分强大,支持自定义render格式、配合hightlight.js,实现语法高亮等等功能。在Naive-UI的插件里,就进行了自定义,解析成了它自有的组件格式,完美实现自给自足。本文实现的功能里,需要用到它的lexer、parser和自定义它的渲染器


插件的简单实现

这里先带着大家简单的实现一个插件,详细的会在后续文章中补充

Project init

新建一个目录(大家随意)useless,然后,我们进行项目初始化:

pnpm create vite useless --template vue

cd useless && pnpm dev

image.png

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

我们进入检查插件的地址查看,就能看到一个这样的界面:

image.png

看起来是不是有点看不懂?没关系,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;

这时候我们来看下页面和插件的检查工具,会是这个样子的: image.png

image.png

可以清晰的在检查工具中看到,文件经过我们定义的add-hello-word插件,对app.vueHelloWorld.vue进行了改造,匹配到<template>标签,并插入了<h1>Hello World</h1>,所以我们在页面里就看到了两个大大的Hello World。至此,我们一个简单的插件就完成了,不过仅仅是最简单的改动,实际上要实现Naive-UI那种结构,还需要很大的改动量,我会一步步来将它们的实现方式来解释清楚。

仓库地址

文档相关的代码,已经放到了tiny-dust/useless (github.com)上,后续我跟根据文章打上对应的tags和分支,方便大家更好的对比查看


下一篇:如何通过自定义的Vite Plugin实现解析md文件

结语

想到写这个文章,既是为了分享,也是为了学习。有些不对的地方,还请大家指正。我的思维受限于自己的认知,所以很难去想到一些奇奇怪怪(对于我来说奇奇怪怪)的操作。过去的一年里,一有时间就刷卡颂、若川、闲强等等大佬的文章,总是震惊于他们怎么理解的这么透彻。随着自己跟着尝试去查看源码,一步步的搜索答案,才发现,原来自己之前的世界那么的小。又想起自己很喜欢的一句话。 我本凡尘微末,却也心向天空

继续加油~