基于vite写一个将md文件渲染为js文件的插件

前言

尤大是这么描述 Vite 的:
「一个基于浏览器原生 ES imports 的开发服务器。
利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个概念,
服务器随起随用。
同时不仅有 Vue 文件支持,还搞定了热更新,而且热更新的速度不会随着模块增多而变慢。
针对生产环境则可以把同一份代码用 rollup 打包。」

这篇博客笔者想分享编写Vite插件【md文件渲染为js文件】的技术栈:md插件源码地址

相关文章

技术栈

  • VUE3.0 前端主流框架(react,vue,angular)之一,更适合开发灵活度高且复杂的应用
  • vue-router Vue.js 官方的路由管理器
  • vite 是一个由原生 ESM 驱动的 Web 开发构建工具
  • github-markdown-css 一个轻量级CSS库代替GitHub Markdown文件样式
  • marked 一个 JavaScript 编写的全功能 Markdown 解析和编译器
  • rollup 一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码

vite插件概念

笔者写这篇文章的时候,还没有关于如何编写 Vite 插件的官方文档。所以,目前只能通过阅读 Vite 源代码,来深入了解如何编写插件。

我们需要先来明确一些相关概念,方便后续的工作:

  • 开发服务器(dev server):浏览器可以处理 js 文件的 ES 模块导入。

但不能直接处理其他文件类型的 import。每次浏览器在代码中遇到 import 的时候,会先通过 Vitedev 服务器编译,然后直接提供给浏览器。

比如:*.vue 文件会先在开发服务器编译,再发送给浏览器。
这些 import 可以是 javascript,vue,css 文件。也可以是其他类型的文件,不过需要在 vite 中指定变异工具。

这就是为什么 Vite 开发服务器如此有用 —— 它解决了浏览器的限制。

  • rollup 生产包(rollup production bundle):对于静态内容,在生产版本中没有 vite 开发服务器可用。所以,vite 使用rollup 打包生产环境的代码。

  • 自定义块(custom block):有时候,你使用的其他库可以向 vue 文件中添加自定义块,例如<docs><story>。可以使用 Vite 来制定如何处理自定义块。

创建初始插件

Vite插件只是一个对象,我们可以先返回一个空对象。

创建一个用作为插件的js文件plugins/md.ts:

export function md()  {
 return {}
}

然后,在 vite.config.js 这样使用:

import { md } from "./plugins/md";
export default {
  plugins: [md()],
}

细心的小伙伴可能会发现,我们导出了一个函数而不是对象。 这是为了方便以后在插件中添加自定义选项。
比如:想实现指定一个自定义MD文件样式功能,可以这样 viteAutoRoute({ h1_color: 'red' })

开发环境

现在,来使用我们之前创建的那些md文件

使用 vite 插件的选项configureServer

// @ts-nocheck
import path from "path";
import fs from "fs";
import marked from "marked";

const mdToJs = (str) => {
  const content = JSON.stringify(marked(str));
  return `export default ${content}`;
};

export function md() {
  return {
    configureServer: [
      // 用于开发
      async ({ app }) => {
        app.use(async (ctx, next) => {
          // koa
          if (ctx.path.endsWith(".md")) {
            ctx.type = "js";
            const filePath = path.join(process.cwd(), ctx.path);
            ctx.body = mdToJs(fs.readFileSync(filePath).toString());
          } else {
            await next();
          }
        });
      },
    ]
  };
}

首先,创建一个函数 mdToJs ,其中包含路由 js 文件的所需内容,主要功能是使用marked.js库将字符串渲染为标准的md文件。然后,我们创建 configureServer 一个数组,用于 vite dev 服务器中的中间件。

每当 vite 导入 javascript 或者 vue 文件的时候,它都会向该文件发送请求到其 dev 服务器,在必要时进行一些转换,然后以浏览器可以处理的形式发送回去。

当它遇到类似import Intro from "./markdown/intro.md";

它会要求文件后缀名为.md

因此,我们要做的是拦截该请求并返回我们生成 moduleContent 的主体,并将其 type 声明为 js

最后,我们将此 configureServer 数组添加到返回的对象中,以供 Vite 使用。

Vite 看到这一点后,将我们的列表(共1个)中间件与自己的中间件合并。

现在,我们可以就在自己的Markdowm组件中使用这些使用md文件啦:

<template>
  <article class="markdown-body" v-html="content"></article>
</template>

<script lang="ts">
import { ref } from "vue";
export default {
  props: {
    content: {
      type: String,
      required: true,
    },
  },
};
</script>

现在,我们可以运行 yarn dev 来查看路由是怎么工作的, http://localhost:3000/#/doc/intro

生产环境

不过,当我们运行 yarn build 它时,它不会使用我们刚刚完成的 configureServer, 因为在生产环境中它使用 rollup 编译而不是 Vitedev 服务器

因此,我们需要添加一些其他配置让它其在生产环境中也能正常工作:

// @ts-nocheck
import path from "path";
import fs from "fs";
import marked from "marked";

const mdToJs = (str) => {
  const content = JSON.stringify(marked(str));
  return `export default ${content}`;
};

export function md() {
  return {
	// 这块是之前定义的 configureServer 代码
    transforms: [
      {
        // 用于 rollup // 插件
        test: (context) => context.path.endsWith(".md"),
        transform: ({ code }) => mdToJs(code),
      },
    ],
  };
}

在这里,我们使用了 vite 的插件选项 transforms。这使我们可以定义 rollup 的插件功能。

我们使用带有.md后缀名的模块,并返回需要的 js 内容。

本质上,它和开发服务器做的是相同的事情。

现在,我们的自动路由在本地开发中和生产环境中都起作用啦

路由渲染

插件完成后,通过h函数渲染到对应的页面中。

import Intro from "./markdown/intro.md";
import Install from "./markdown/install.md";
import Getstarted from "./markdown/get-started.md";

import { h } from "vue";
import Markdown from "./components/Markdown.vue";
const md = (string) => h(Markdown, { content: string, key: string });

const history = createWebHashHistory();
export const router = createRouter({
  history: history,
  routes: [
    { path: "/", component: Home },
    {
      path: "/doc",
      component: Doc,
      children: [
        { path: "", redirect: "/doc/intro" },
        { path: "intro", component: md(Intro) },
        { path: "get-started", component: md(Getstarted) },
        { path: "install", component: md(Install) },
      ],
    },
  ],
});

总结

希望大家有了解到 Vite 的工作原理,还有如何编写一些基本的插件。

下面是完整功能代码的地址:

kaite-ui官网页面kaite-ui官网页面
github开源地址github开源地址

Logo

前往低代码交流专区

更多推荐