自定义一个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,然后借助nodepath,获取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文件中加一条信息:

在这里插入图片描述

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

在这里插入图片描述

4.代码地址

详细代码见https://gitee.com/wlmProject/small-vite-plugin

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太阳与星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值