Vite devient de plus en plus populaire parmi les développeurs, mais comme la communauté n'est pas aussi grande (comme dans Webpack ), vous devrez peut-être créer votre propre plugin personnalisé pour résoudre vos problèmes. Dans cet article, nous verrons comment créer un plugin pour Vite et je décomposerai mon propre plugin.
Pour créer un plugin, il est important de savoir que Vite utilise des systèmes de build différents pour le serveur de développement (commande vite
) et le bundle (commande vite build
).
Pour le serveur de développement, il utilise esbuild avec les modules ES natifs, qui sont pris en charge par les navigateurs modernes, et nous n'avons pas besoin de regrouper le code dans un seul fichier, et cela nous donne un HRM (Hot Module Remplacement) rapide.
Pour le bundle, il utilise un rollup.js car il est flexible et dispose d'un vaste écosystème ; il permet la création de lots de production hautement optimisés avec différents formats de sortie.
L'interface du plugin de Vite est basée sur celle de Rollup mais avec des options et des crochets supplémentaires pour travailler avec le serveur de développement.
Lorsque vous créez un plugin, vous pouvez l'intégrer dans votre vite.config.js
. Il n'est pas nécessaire de créer un nouveau package pour cela. Une fois que vous voyez qu'un plugin a été utile dans vos projets, pensez à le partager avec la communauté et à contribuer à l'écosystème Vite.
De plus, étant donné que rollup.js dispose d'une communauté et d'un écosystème plus importants, vous pourriez envisager de créer un plugin pour rollup.js, et cela fonctionnera tout aussi bien dans Vite. Ainsi, si la fonctionnalité de votre plugin ne fonctionne que pour le bundle, vous pouvez utiliser le plugin rollup.js au lieu de Vite, et les utilisateurs peuvent utiliser votre plugin rollup dans leurs projets Vite sans aucun problème.
Si vous créez un plugin pour rollup, vous couvrirez davantage d'utilisateurs qui utilisent uniquement rollup.js. Si votre plugin affectera le serveur de développement, alors vous optez pour le plugin Vite.
Commençons par créer le plugin directement dans 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(), ], });
Dans cet exemple, j'ai créé un plugin appelé myPlugin
qui imprime la configuration Vite dès qu'elle est résolue dans la console dans les deux étapes : serveur de développement et bundle. Si je souhaite imprimer la configuration uniquement en mode serveur de développement, je dois alors ajouter apply: 'serve'
et apply: 'build'
pour le bundle.
// 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(), ], });
De plus, je peux renvoyer une gamme de plugins ; c'est utile pour séparer les fonctionnalités du serveur de développement et du bundle :
// 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(), ], });
Et c'est à peu près tout; vous pouvez facilement ajouter de petits plugins à la configuration Vite. Et si un plugin devient trop gros, je préfère le déplacer vers un autre fichier ou même créer un package.
Si vous avez besoin de quelque chose de plus complexe, il existe de nombreux hooks utiles que vous pouvez explorer dans la documentation de Vite. Mais à titre d'exemple, décomposons mon propre plugin ci-dessous.
J'ai donc un plugin pour créer des sprites SVG basés sur des fichiers d'icônes - vite-plugin-svg-spritemap .
Le but est de récupérer toutes les icônes .svg
dans le dossier src/icons
et de collecter leur contenu dans un seul fichier .svg
appelé sprite SVG. Commençons par l'étape du bundle :
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
nous permet d'obtenir la configuration lorsqu'il est résolu de l'utiliser dans les prochains hooks ;
Le hook writeBundle
est appelé une fois le processus de regroupement terminé, et ici, je vais créer le fichier sprite.svg
;
La fonction getSpriteContent
renvoie une chaîne de sprites SVG préparés basés sur le modèle src/icons/*.svg
. Je n'irai pas plus loin avec celui-ci ; vous pouvez consulter mon autre article expliquant tout le processus de génération de sprite SVG ;
Ensuite, je crée le chemin absolu vers le sprite.svg
pour y insérer le contenu du sprite SVG avec path.resolve()
, je m'assure que ce fichier existe (ou j'en crée un) avec fs.ensureFileSync
., et j'y écris le contenu du sprite SVG. .
Passons maintenant à la partie la plus intéressante : l’étape du serveur de développement. Je ne peux pas utiliser writeBundle
ici, et je ne peux pas héberger de fichiers lorsque le serveur de développement est en cours d'exécution, nous devons donc utiliser un middleware de serveur pour intercepter la requête adressée à 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); }); }; }, }; }
configureServer
est un hook pour configurer le serveur de développement. Il se déclenche avant l'installation du middleware interne de Vite ; dans mon cas, je dois ajouter un middleware personnalisé après le middleware interne, donc je renvoie une fonction ;
Pour ajouter un middleware personnalisé afin de détecter chaque requête adressée au serveur de développement, j'utilise server.middlewares.use()
. J'en ai besoin pour détecter les requêtes avec l'URL [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg)
, afin de pouvoir émuler le comportement des fichiers ;
Si l'URL de la requête n'est pas /sprite.svg
- passez au middleware suivant (c'est-à-dire, passez le contrôle au gestionnaire suivant dans la chaîne) ;
Pour préparer le contenu du fichier, je mets le résultat de getSpriteContent
dans le sprite
variable et l'envoie en réponse avec les en-têtes configurés (type de contenu et statut HTTP 200).
En conséquence, j'ai simulé le comportement des fichiers.
Mais si des fichiers dans src/icons
sont modifiés, supprimés ou ajoutés, nous devons redémarrer le serveur pour générer un nouveau contenu de sprite via getSpriteContent
; pour cela, j'utiliserai la bibliothèque de surveillance de fichiers - chokidar . Ajoutons les gestionnaires chokidar au code :
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); }); }; }, }; }
Comme vous pouvez le constater, l’API de création de plugin n’est pas vraiment compliquée. Il vous suffit de trouver des hooks de Vite ou Rollup qui correspondront à vos tâches. Dans mon exemple, j'utilise writeBundle
de Rollup.js (comme je l'ai dit, il est utilisé pour générer le bundle) et configureServer
de Vite car Rollup.js ne prend pas en charge le serveur de développement natif.
Dans le cas du writeBundle
c'était très simple, nous avons pris le contenu du sprite SVG et l'avons mis dans un fichier. Dans le cas du serveur de développement, je ne comprenais pas pourquoi je ne pouvais pas faire de même ; J'ai regardé les plugins d'autres auteurs, et ils font tous à peu près la même chose.
J'utilise donc configureServer
et via l'argument server
, j'ajoute un middleware qui déclenche chaque requête au serveur de développement en interceptant la requête sprite.svg
.
Comme je l'ai déjà mentionné, pour créer des plugins plus utiles, vous devez explorer les hooks. Ils sont expliqués en détail dans la documentation :
https://vitejs.dev/guide/api-plugin#universal-hooks
https://vitejs.dev/guide/api-plugin#vite-special-hooks
En termes de dénomination, Vite a certaines conventions pour les plugins, vous feriez donc mieux de les vérifier avant de terminer. Voici quelques points clés :
vite-plugin-
;
vite-plugin
dans package.json ;
vite-plugin-vue-
, vite-plugin-react-
, vite-plugin-svelte-
).Si vous décidez de publier votre plugin en NPM, ce que je recommande car le partage des connaissances et des expertises est un principe fondamental de la communauté informatique, favorisant la croissance collective. Pour savoir comment publier et maintenir votre package, consultez mon guide → Le moyen le plus simple de créer un package NPM .
Je recommande également fortement de soumettre votre plugin à la liste de la communauté vite - Awesome-vite . Beaucoup de personnes y recherchent les plugins les plus adaptés, et ce serait une belle opportunité de contribuer à l’écosystème Vite ! Le processus de soumission de votre plugin est simple : assurez-vous simplement de respecter les conditions et de créer une pull request. Vous pouvez trouver la liste des termes ici .
Dans l'ensemble, il doit être spécifique à Vite (et non au rollup), open source et disposer d'une bonne documentation. Bonne chance avec vos plugins !