在开发者中越来越受欢迎,但由于社区没有 那么大,您可能需要创建自己的自定义插件来解决您的问题。在这篇文章中,我们将讨论如何为 创建一个插件,并且我将分解我自己的插件。 Vite Webpack Vite 插件在 Vite 中的工作原理 要创建插件,重要的是要知道 Vite 对开发服务器(命令 )和捆绑包(命令 )使用不同的构建系统。 vite vite build 对于开发服务器,它使用带有原生 ES 模块的 ,这些模块受到现代浏览器的支持,我们不需要将代码捆绑到单个文件中,并且它为我们提供了快速的 HRM(热模块替换)。 esbuild 对于捆绑包,它使用 因为它很灵活并且拥有庞大的生态系统;它允许创建具有不同输出格式的高度优化的生产包。 rollup.js, Vite 的插件界面基于 Rollup 的,但具有用于与开发服务器配合使用的附加选项和挂钩。 创建一个基本插件 创建插件时,您可以将其内联到 中。无需为其创建新包。一旦您发现某个插件在您的项目中有用,请考虑与社区分享并为 Vite 生态系统做出贡献。 vite.config.js 另外,由于 rollup.js 拥有更大的社区和生态系统,您可以考虑为 rollup.js 创建一个插件,它在 Vite 中也能正常工作。因此,如果您的插件功能仅适用于捆绑包,您可以使用 rollup.js 插件而不是 Vite,并且用户可以在其 Vite 项目中使用您的 rollup 插件,不会出现任何问题。 如果您为 rollup 创建插件,您将覆盖更多仅使用 rollup.js 的用户。如果您的插件会影响开发服务器,那么您可以使用 Vite 插件。 让我们开始直接在 中创建插件: vite.config.ts // vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin { return { name: 'my-plugin', configResolved(config) { console.log(config); }, }; } export default defineConfig({ plugins: [ myPlugin(), ], }); 在此示例中,我创建了一个名为 的插件,一旦在控制台中的两个阶段(开发服务器和捆绑包)中解析 Vite 配置,该插件就会打印它。如果我只想在开发服务器模式下打印配置,那么我应该为捆绑添加 和 。 myPlugin apply: 'serve' apply: 'build' // vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin { return { name: 'my-plugin', apply: 'serve', configResolved(config) { console.log(config); }, }; } export default defineConfig({ plugins: [ myPlugin(), ], }); 另外,我可以返回一组插件;它对于分离开发服务器和捆绑包的功能很有用: // vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin[] { return [ { name: 'my-plugin:serve', apply: 'serve', configResolved(config) { console.log('dev server:', config); }, }, { name: 'my-plugin:build', apply: 'build', configResolved(config) { console.log('bundle:', config); }, }, ]; } export default defineConfig({ plugins: [ myPlugin(), ], }); 差不多就是这样了;您可以轻松地将小插件添加到 Vite 配置中。如果插件太大,我更喜欢将其移动到另一个文件,甚至创建一个包。 如果您需要更复杂的东西,您可以在 Vite 文档中探索许多有用的钩子。但作为一个例子,让我们在下面分解我自己的插件。 分解一个真正的插件 所以,我有一个基于图标文件创建 SVG 精灵的插件 - 。 vite-plugin-svg-spritemap 目标是获取 文件夹中的所有图标 ,并将其内容收集到单个 文件(称为 SVG sprite)中。让我们从捆绑阶段开始: src/icons .svg .svg import { Plugin, ResolvedConfig } from 'vite'; import path from 'path'; import fs from 'fs-extra'; function myPlugin(): Plugin { let config: ResolvedConfig; return { name: 'my-plugin:build', apply: 'build', async configResolved(_config) { config = _config; }, writeBundle() { const sprite = getSpriteContent({ pattern: 'src/icons/*.svg' }); const filePath = path.resolve(config.root, config.build.outDir, 'sprite.svg'); fs.ensureFileSync(filePath); fs.writeFileSync(filePath, sprite); }, }; } Hook 允许我们在决定在下一个钩子中使用它时获取配置; configResolved 钩子在捆绑过程完成后被调用,在这里,我将创建 文件; writeBundle sprite.svg 函数返回基于 模式准备的 SVG 精灵的字符串。我不会更深入地讨论这一点;你可以看看我的另一篇 ; getSpriteContent src/icons/*.svg 文章解释了SVG精灵生成的整个过程 然后,我创建 的绝对路径,以使用 将 SVG 精灵内容放入其中,使用 确保该文件存在(或创建一个),然后将 SVG 精灵内容写入其中。 sprite.svg path.resolve() fs.ensureFileSync 现在,最有趣的部分 - 开发服务器阶段。我在这里无法使用 ,并且在开发服务器运行时无法托管文件,因此我们需要使用服务器中间件来捕获对 的请求。 writeBundle sprite.svg import { Plugin, ResolvedConfig } from 'vite'; function myPlugin(): Plugin { let config: ResolvedConfig; return { name: `my-plugin:serve`, apply: 'serve', async configResolved(_config) { config = _config; }, configureServer(server) { // (1) return () => { server.middlewares.use(async (req, res, next) => { // (2) if (req.url !== '/sprite.svg') { return next(); // (3) } const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor }); res.writeHead(200, { // (4) 'Content-Type': 'image/svg+xml, charset=utf-8', 'Cache-Control': 'no-cache', }); res.end(sprite); }); }; }, }; } 是一个用于配置开发服务器的钩子。在Vite内部中间件安装之前触发;就我而言,我需要在内部中间件之后添加自定义中间件,因此我返回一个函数; configureServer 要添加自定义中间件以捕获对开发服务器的每个请求,我使用 。我需要它来检测 URL 的请求,这样我就可以模拟文件行为; server.middlewares.use() [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg) 如果请求 URL 不是 - 跳到下一个中间件(即,将控制权传递给链中的下一个处理程序); /sprite.svg 为了准备文件内容,我将 的结果放入变量 中,并将其作为带有配置标头(内容类型和 200 HTTP 状态)的响应发送。 getSpriteContent sprite 结果,我模拟了文件行为。 但是如果 中的文件被更改、删除或添加,我们应该重新启动服务器以通过 生成新的精灵内容;为此,我将使用文件监视库 - 。让我们将 chokidar 处理程序添加到代码中: src/icons getSpriteContent chokidar import { Plugin, ResolvedConfig } from 'vite'; import chokidar from 'chokidar'; function myPlugin(): Plugin { let config: ResolvedConfig; let watcher: chokidar.FSWatcher; // Defined variable for chokidar instance. return { name: `my-plugin:serve`, apply: 'serve', async configResolved(_config) { config = _config; }, configureServer(server) { function reloadPage() { // Function that sends a signal to reload the server. server.ws.send({ type: 'full-reload', path: '*' }); } watcher = chokidar .watch('src/icons/*.svg', { // Watch src/icons/*.svg cwd: config.root, // Define project root path ignoreInitial: true, // Don't trigger chokidar on instantiation. }) .on('add', reloadPage) // Add listeners to add, modify, delete. .on('change', reloadPage) .on('unlink', reloadPage); return () => { server.middlewares.use(async (req, res, next) => { if (req.url !== '/sprite.svg') { return next(); } const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor }); res.writeHead(200, { 'Content-Type': 'image/svg+xml, charset=utf-8', 'Cache-Control': 'no-cache', }); res.end(sprite); }); }; }, }; } 正如您所看到的,插件创建的 API 并不复杂。您只需要从 Vite 或 Rollup 中找到适合您任务的钩子即可。在我的示例中,我使用 Rollup.js 中的 (正如我所说,它用于生成包),并使用 Vite 中的 ,因为 Rollup.js 没有本机开发服务器支持。 writeBundle configureServer 对于 来说,它非常简单,我们获取 SVG 精灵内容并将其放入文件中。就开发服务器而言,我很困惑为什么我不能做同样的事情;我查看了其他作者的插件,它们的作用都差不多。 writeBundle 因此,我使用 并通过 参数添加中间件,通过拦截 请求来触发对开发服务器的每个请求。 configureServer server sprite.svg 使用 Vite Hooks 正如我之前提到的,要创建更有用的插件,您需要探索挂钩。它们在文档中有详细解释: https://vitejs.dev/guide/api-plugin#universal-hooks https://vitejs.dev/guide/api-plugin#vite-specific-hooks 如何命名插件 在命名方面,Vite 对插件有一些约定,大家最好先检查一下再完成。以下是一些要点: Vite 插件应该有一个唯一的名称,并带有 前缀; vite-plugin- package.json 中包含 关键字; vite-plugin 在插件文档中包含一个部分,解释为什么它是 Vite-only 插件(例如,它使用 Vite 特定的插件挂钩); 如果您的插件仅适用于特定框架,请将其名称作为前缀的一部分( 、 、 )。 vite-plugin-vue- vite-plugin-react- vite-plugin-svelte- 如何发布和共享您的插件 如果您决定在 NPM 中发布您的插件,我建议这样做,因为共享知识和专业知识是 IT 社区的基本原则,可以促进集体成长。要了解如何发布和维护包,请查看我的指南 → 。 创建 NPM 包的最简单方法 我还强烈建议将您的插件提交到 vite 的社区列表 - 。很多人都在寻找最合适的插件,这将是为Vite生态做出贡献的绝佳机会!在那里提交插件的过程很简单 - 只需确保满足条款并创建拉取请求即可。您可以 找到术语列表。 Awesome-vite 在此处 总体来说,它需要针对Vite(而不是rollup),开源,并且有良好的文档。祝你的插件好运!