Vite wird bei Entwicklern immer beliebter, aber da die Community nicht so groß ist (wie bei Webpack ), müssen Sie möglicherweise Ihr eigenes benutzerdefiniertes Plugin erstellen, um Ihre Probleme zu lösen. In diesem Artikel besprechen wir, wie man ein Plugin für Vite erstellt, und ich werde mein eigenes Plugin aufschlüsseln.
Um ein Plugin zu erstellen, ist es wichtig zu wissen, dass Vite unterschiedliche Build-Systeme für den Entwicklungsserver (Befehl vite
) und das Bundle (Befehl vite build
) verwendet.
Für den Entwicklungsserver wird esbuild mit nativen ES-Modulen verwendet, die von modernen Browsern unterstützt werden. Wir müssen den Code nicht in einer einzigen Datei bündeln und erhalten einen schnellen HRM (Hot Module Replacement).
Für das Bundle wird rollup.js verwendet, da es flexibel ist und über ein großes Ökosystem verfügt. Es ermöglicht die Erstellung hochoptimierter Produktionspakete mit unterschiedlichen Ausgabeformaten.
Die Plugin-Schnittstelle von Vite basiert auf der von Rollup, verfügt jedoch über zusätzliche Optionen und Hooks für die Arbeit mit dem Entwicklungsserver.
Wenn Sie ein Plugin erstellen, können Sie es in Ihre vite.config.js
integrieren. Es ist nicht erforderlich, dafür ein neues Paket zu erstellen. Wenn Sie feststellen, dass ein Plugin in Ihren Projekten nützlich war, sollten Sie darüber nachdenken, es mit der Community zu teilen und zum Vite-Ökosystem beizutragen.
Da rollup.js außerdem über eine größere Community und ein größeres Ökosystem verfügt, könnten Sie darüber nachdenken, ein Plugin für rollup.js zu erstellen, das in Vite genauso gut funktionieren wird. Wenn Ihre Plugin-Funktionalität also nur für das Bundle funktioniert, können Sie anstelle von Vite das rollup.js-Plugin verwenden und Benutzer können Ihr Rollup-Plugin problemlos in ihren Vite-Projekten verwenden.
Wenn Sie ein Plugin für Rollup erstellen, decken Sie mehr Benutzer ab, die nur rollup.js verwenden. Wenn Ihr Plugin Auswirkungen auf den Entwicklungsserver hat, entscheiden Sie sich für das Vite-Plugin.
Beginnen wir mit der Erstellung des Plugins direkt in 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(), ], });
In diesem Beispiel habe ich ein Plugin namens myPlugin
erstellt, das die Vite-Konfiguration druckt, sobald sie in der Konsole in beiden Phasen aufgelöst wird: Entwicklungsserver und Bundle. Wenn ich die Konfiguration nur im Dev-Server-Modus drucken möchte, sollte ich apply: 'serve'
und apply: 'build'
für Bundle hinzufügen.
// 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(), ], });
Außerdem kann ich ein Array von Plugins zurückgeben; Dies ist nützlich, um die Funktionalität für den Entwicklungsserver und das Bundle zu trennen:
// 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(), ], });
Und das ist so ziemlich alles; Sie können der Vite-Konfiguration problemlos kleine Plugins hinzufügen. Und wenn ein Plugin zu groß wird, verschiebe ich es lieber in eine andere Datei oder erstelle sogar ein Paket.
Wenn Sie etwas Komplexeres benötigen, finden Sie in der Dokumentation von Vite viele nützliche Hooks, die Sie erkunden können. Aber als Beispiel wollen wir unten mein eigenes Plugin aufschlüsseln.
Ich habe also ein Plugin zum Erstellen von SVG-Sprites basierend auf Symboldateien – vite-plugin-svg-spritemap .
Das Ziel besteht darin, alle .svg
Symbole im Ordner src/icons
zu erfassen und ihren Inhalt in einer einzigen .svg
Datei zusammenzufassen, die als SVG-Sprite bezeichnet wird. Beginnen wir mit der Bundle-Phase:
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
ermöglicht es uns, die Konfiguration abzurufen, wenn sie aufgelöst ist, um sie in den nächsten Hooks zu verwenden;
Der writeBundle
Hook wird aufgerufen, nachdem der Bündelungsprozess abgeschlossen ist, und hier werde ich die Datei sprite.svg
erstellen;
Die Funktion getSpriteContent
gibt eine Zeichenfolge vorbereiteter SVG-Sprites basierend auf dem Muster src/icons/*.svg
zurück. Ich werde hier nicht näher darauf eingehen; Sie können sich meinen anderen Artikel ansehen, in dem der gesamte Prozess der SVG-Sprite-Generierung erläutert wird .
Dann erstelle ich mit path.resolve()
den absoluten Pfad zur sprite.svg
, in die der SVG-Sprite-Inhalt eingefügt werden soll, stelle mit fs.ensureFileSync
sicher, dass die Datei existiert (oder erstelle eine solche), und schreibe den SVG-Sprite-Inhalt hinein .
Nun zum interessantesten Teil – der Entwicklungsserverphase. Ich kann writeBundle
hier nicht verwenden und ich kann keine Dateien hosten, wenn der Entwicklungsserver läuft, daher müssen wir Server-Middleware verwenden, um die Anfrage an sprite.svg
abzufangen.
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); }); }; }, }; }
configureServer
ist ein Hook zum Konfigurieren des Entwicklungsservers. Es wird ausgelöst, bevor die interne Middleware von Vite installiert wird. In meinem Fall muss ich nach der internen Middleware benutzerdefinierte Middleware hinzufügen, also gebe ich eine Funktion zurück.
Um benutzerdefinierte Middleware hinzuzufügen, um jede Anfrage an den Entwicklungsserver abzufangen, verwende ich server.middlewares.use()
. Ich benötige es, um Anfragen mit der URL [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg)
zu erkennen, damit ich das Dateiverhalten emulieren kann;
Wenn die Anforderungs-URL nicht /sprite.svg
lautet, fahren Sie mit der nächsten Middleware fort (dh übergeben Sie die Steuerung an den nächsten Handler in der Kette).
Um Dateiinhalte vorzubereiten, füge ich das Ergebnis von getSpriteContent
in die Variable sprite
ein und sende es als Antwort mit konfigurierten Headern (Inhaltstyp und 200 HTTP-Status).
Als Ergebnis habe ich das Dateiverhalten simuliert.
Wenn jedoch Dateien in src/icons
geändert, gelöscht oder hinzugefügt werden, sollten wir den Server neu starten, um über getSpriteContent
neuen Sprite-Inhalt zu generieren. Dafür verwende ich die Dateiüberwachungsbibliothek – chokidar . Fügen wir dem Code Chokidar-Handler hinzu:
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); }); }; }, }; }
Wie Sie sehen, ist die API zur Plugin-Erstellung nicht wirklich kompliziert. Sie müssen nur Haken von Vite oder Rollup finden, die zu Ihren Aufgaben passen. In meinem Beispiel verwende ich writeBundle
von Rollup.js (wie gesagt, es wird zum Generieren des Bundles verwendet) und configureServer
von Vite, da Rollup.js keine native Entwicklungsserverunterstützung bietet.
Im Fall des writeBundle
war es ganz einfach: Wir haben den SVG-Sprite-Inhalt genommen und ihn in eine Datei geschrieben. Im Fall des Entwicklungsservers verwirrte es mich, warum ich nicht dasselbe tun konnte; Ich habe mir die Plugins anderer Autoren angesehen und sie alle tun ungefähr das Gleiche.
Also verwende ich configureServer
und füge über das server
Argument Middleware hinzu, die jede Anfrage an den Entwicklungsserver auslöst, indem sie die sprite.svg
Anfrage abfängt.
Wie ich bereits erwähnt habe, müssen Sie die Hooks erkunden, um nützlichere Plugins zu erstellen. Sie werden in der Dokumentation ausführlich erläutert:
https://vitejs.dev/guide/api-plugin#universal-hooks
https://vitejs.dev/guide/api-plugin#vite-special-hooks
Was die Benennung angeht, hat Vite einige Konventionen für Plugins, also schauen Sie sich das besser an, bevor Sie fertig sind. Hier sind einige wichtige Punkte:
vite-plugin-
haben;
vite-plugin
in package.json ein.
vite-plugin-vue-
, vite-plugin-react-
, vite-plugin-svelte-
).Wenn Sie sich entscheiden, Ihr Plugin in NPM zu veröffentlichen, empfehle ich dies, da der Austausch von Wissen und Fachwissen ein Grundprinzip der IT-Community ist und das gemeinsame Wachstum fördert. Um zu erfahren, wie Sie Ihr Paket veröffentlichen und verwalten, lesen Sie meinen Leitfaden → Der einfachste Weg, ein NPM-Paket zu erstellen .
Ich empfehle außerdem dringend, Ihr Plugin an die Community-Liste von vite zu senden – awesome-vite . Viele Leute suchen dort nach den am besten geeigneten Plugins und es wäre eine großartige Gelegenheit, einen Beitrag zum Vite-Ökosystem zu leisten! Der Prozess, Ihr Plugin dort einzureichen, ist einfach – stellen Sie einfach sicher, dass Sie die Bedingungen erfüllen, und erstellen Sie eine Pull-Anfrage. Die Liste der Begriffe finden Sie hier .
Insgesamt muss es spezifisch für Vite (kein Rollup) und Open Source sein und über eine gute Dokumentation verfügen. Viel Glück mit deinen Plugins!