Appearance
API de plugin
Les plugins Vite étendent l'interface de plugin bien conçue de Rollup avec quelques options supplémentaires spécifiques à Vite. En conséquence, vous pouvez écrire un plugin Vite une fois et le faire fonctionner à la fois pour dev et build.
Il est recommandé de parcourir la [documentation des plugins de Rollup] (https://rollupjs.org/guide/en/#plugin-development) avant de lire les sections ci-dessous.
Création d'un plugin
Vite s'efforce d'offrir des modèles établis dès le départ, donc avant de créer un nouveau plugin, assurez-vous de vérifier le Guide des fonctionnalités pour voir si votre besoin est couvert. Examinez également les plugins communautaires disponibles, sous la forme d'un [plugin compatible Rollup] (https://github.com/rollup/awesome) et de [plugins spécifiques à Vite] (https://github.com/vitejs/awesome-vite#plugins).
Lorsque vous créez un plugin, vous pouvez le mettre en ligne dans votre vite.config.js
. Il n'est pas nécessaire de créer un nouveau paquet pour lui. Lorsque vous voyez qu'un plugin a été utile dans vos projets, pensez à le partager pour aider les autres [dans l'écosystème] (https://chat.vitejs.dev).
TIP
Lorsque vous apprenez, déboguez ou créez des plugins, nous vous suggérons d'inclure vite-plugin-inspect dans votre projet. Il vous permet d'inspecter l'état intermédiaire des plugins Vite. Après l'installation, vous pouvez visiter localhost:5173/__inspect/
pour inspecter les modules et la pile de transformation de votre projet. Consultez les instructions d'installation dans la vite-plugin-inspect docs. !vite-plugin-inspect](/images/vite-plugin-inspect.png)
Conventions
Si le plugin n'utilise pas de hooks spécifiques à Vite et peut être implémenté comme un Plugin Rollup compatible, il est recommandé d'utiliser les conventions de nommage des plugins Rollup.
- Les plugins Rollup doivent avoir un nom clair avec le préfixe
rollup-plugin-
. - Incluez les mots-clés
rollup-plugin
etvite-plugin
dans le package.json.
Cela permet d'utiliser le plugin dans des projets purement Rollup ou WMR.
Pour les plugins Vite uniquement
- Les plugins Vite doivent avoir un nom clair avec le préfixe
vite-plugin-
. - Inclure le mot-clé
vite-plugin
dans le package.json. - Inclure une section dans la documentation du plugin détaillant pourquoi il s'agit d'un plugin Vite uniquement (par exemple, il utilise des hooks de plugin spécifiques à Vite).
Si votre plugin ne fonctionne que pour un framework particulier, son nom doit être inclus dans le préfixe.
vite-plugin-vue-
préfixe pour les plugins Vuevite-plugin-react-
préfixe pour les plugins Reactvite-plugin-svelte-
préfixe pour les plugins Svelte
Voir aussi Virtual Modules Convention.
Configuration des plugins
Les utilisateurs ajouteront des plugins au projet devDependencies
et les configureront en utilisant l'option de tableau plugins
.
js
// vite.config.js
import vitePlugin from 'vite-plugin-feature'.
import rollupPlugin from 'rollup-plugin-feature' (en anglais)
export default defineConfig({
plugins : [vitePlugin(), rollupPlugin()]
})
Les plugins malheureux seront ignorés, ce qui peut être utilisé pour activer ou désactiver facilement les plugins.
plugins
accepte également les préréglages incluant plusieurs plugins comme un seul élément. Ceci est utile pour les fonctionnalités complexes (comme l'intégration de frameworks) qui sont implémentées en utilisant plusieurs plugins. Le tableau sera aplati en interne.
js
// plugin de framework
import frameworkRefresh from 'vite-plugin-framework-refresh'.
import frameworkDevtools from 'vite-plugin-framework-devtools'.
export default function framework(config) {
retourne [frameworkRefresh(config), frameworkDevTools(config)].
}
js
// vite.config.js
import { defineConfig } from 'vite'.
import framework depuis 'vite-plugin-framework'.
export default defineConfig({
plugins : [framework()]
})
Exemples simples
TIP
Il est courant de créer un plugin Vite/Rollup avec une fonction d'usine qui renvoie l'objet du plugin. La fonction peut accepter des options qui permettent aux utilisateurs de personnaliser le comportement du plugin.
Transformer les types de fichiers personnalisés
js
const fileRegex = /\.(mon-fichier-ext)$/
export default function myPlugin() {
return {
name : 'transform-file',
transform(src, id) {
if (fileRegex.test(id)) {
return {
code : compileFileToJS(src),
map : null // fournit la carte de la source si elle est disponible
}
}
}
}
}
Importation d'un fichier virtuel
Voir l'exemple dans la [section suivante] (#virtual-modules-convention).
Convention des modules virtuels
Les modules virtuels sont un schéma utile qui vous permet de transmettre des informations de compilation aux fichiers sources en utilisant la syntaxe d'importation ESM normale.
js
export default function myPlugin() {
const virtualModuleId = "virtual:my-module" (module virtuel)
const resolvedVirtualModuleId = '\0' + virtualModuleId
return {
name : 'my-plugin', // obligatoire, apparaîtra dans les avertissements et les erreurs
resolveId(id) {
si (id === virtualModuleId) {
return resolvedVirtualModuleId
}
},
load(id) {
si (id === resolvedVirtualModuleId) {
return `export const msg = "from virtual module"`
}
}
}
}
Ce qui permet d'importer le module en JavaScript :
js
import { msg } from 'virtual:my-module'.
console.log(msg)
Les modules virtuels dans Vite (et Rollup) sont préfixés par convention avec virtual:
pour le chemin d'accès de l'utilisateur. Si possible, le nom du plugin doit être utilisé comme un espace de nom pour éviter les collisions avec d'autres plugins dans l'écosystème. Par exemple, un vite-plugin-posts
pourrait demander aux utilisateurs d'importer un module virtuel virtual:posts
ou virtual:posts/helpers
pour obtenir des informations sur le temps de construction. En interne, les plugins qui utilisent des modules virtuels doivent préfixer l'ID du module par \0
lors de la résolution de l'id, une convention de l'écosystème rollup. Cela empêche les autres plugins d'essayer de traiter l'identifiant (comme la résolution de nœuds), et les fonctionnalités principales comme les cartes de source peuvent utiliser cette information pour différencier les modules virtuels des fichiers ordinaires. \0
n'est pas un caractère autorisé dans les URL d'importation, nous devons donc les remplacer pendant l'analyse de l'importation. Un identifiant virtuel \0{id}
finit par être encodé comme /@id/__x00__{id}
pendant le développement dans le navigateur. L'identifiant sera décodé avant d'entrer dans le pipeline des plugins, il ne sera donc pas vu par le code des hooks des plugins.
Notez que les modules directement dérivés d'un fichier réel, comme dans le cas d'un module de script dans un Single File Component (comme un SFC .vue ou .svelte) n'ont pas besoin de suivre cette convention. Les SFCs génèrent généralement un ensemble de sous-modules lorsqu'ils sont traités, mais le code de ces derniers peut être mappé sur le système de fichiers. L'utilisation de ``0` pour ces sous-modules empêcherait le fonctionnement correct des cartes de source.
Crochets universels
Pendant le développement, le serveur de développement Vite crée un conteneur de plugin qui invoque Rollup Build Hooks de la même manière que Rollup.
Les hooks suivants sont appelés une fois au démarrage du serveur :
Les hooks suivants sont appelés à chaque requête de module entrante :
Les hooks suivants sont appelés lorsque le serveur est fermé :
Notez que le hook moduleParsed
n'est pas appelé pendant le dev, car Vite évite les analyses AST complètes pour de meilleures performances.
Les Output Generation Hooks (sauf closeBundle
) ne sont pas appelés pendant le développement. Vous pouvez considérer que le serveur de développement de Vite appelle seulement rollup.rollup()
sans appeler bundle.generate()
.
Crochets spécifiques à Vite
Les plugins Vite peuvent aussi fournir des hooks qui servent des objectifs spécifiques à Vite. Ces hooks sont ignorés par Rollup.
config
Type:
(config : UserConfig, env : { mode : string, command : string }) => UserConfig | null | void
Kind:
async
,sequential
Modifie la configuration de Vite avant qu'elle ne soit résolue. Le hook reçoit la configuration utilisateur brute (options CLI fusionnées avec le fichier de configuration) et la config env courante qui expose le
mode
et lacommande
utilisés. Il peut retourner un objet de configuration partielle qui sera profondément fusionné dans la configuration existante, ou directement muter la configuration (si la fusion par défaut ne permet pas d'obtenir le résultat souhaité).Exemple:
js// renvoie une configuration partielle (recommandé) const partialConfigPlugin = () => ({ name : 'return-partial', config : () => ({ résoudre : { alias : { foo : 'bar } } }) }) // Mutation directe de la configuration (à utiliser uniquement lorsque la fusion ne fonctionne pas) const mutateConfigPlugin = () => ({ nom : 'mutate-config', config(config, { command }) { if (command === 'build') { config.root = 'foo' } } })
Remarque
Les plugins utilisateurs sont résolus avant l'exécution de ce hook, donc l'injection d'autres plugins dans le hook
config
n'aura aucun effet.
configResolved
Type:
(config : ResolvedConfig) => void | Promise<void>
Kind:
async
,parallel
Appelé après la résolution de la configuration Vite. Utilisez ce hook pour lire et stocker la configuration finale résolue. Il est également utile lorsque le plugin doit faire quelque chose de différent en fonction de la commande exécutée.
Exemple:
jsconst examplePlugin = () => { let config return { name : 'read-config', configResolved(resolvedConfig) { // stocke la configuration résolue config = resolvedConfig }, // utilisation de la configuration stockée dans d'autres hooks transform(code, id) { if (config.command === 'serve') { // dev : plugin invoqué par le serveur de développement } else { // build : plugin invoqué par Rollup } } } }
Notez que la valeur de
command
estserve
dans dev (dans le clivite
,vite dev
, etvite serve
sont des alias).
configureServer
Type:
(server : ViteDevServer) => (() => void) | void | Promise<(() => void) | void>
Kind:
async
,sequential
Voir aussi:** ViteDevServer
Crochet pour configurer le serveur de développement. Le cas d'utilisation le plus courant est l'ajout d'intergiciels personnalisés à l'application interne connect :
jsconst myPlugin = () => ({ nom : 'configure-server', configureServer(server) { server.middlewares.use((req, res, next) => { // traitement personnalisé de la demande... }) } })
Injection d'un middleware pour les postes
Le hook
configureServer
est appelé avant que les middlewares internes soient installés, donc les middlewares personnalisés seront exécutés avant les middlewares internes par défaut. Si vous voulez injecter un middleware après les middlewares internes, vous pouvez retourner une fonction deconfigureServer
, qui sera appelée après l'installation des middlewares internes :jsconst myPlugin = () => ({ nom : 'configure-server', configureServer(server) { // retourne un post hook qui est appelé après que les middlewares internes soient // installés return () => { server.middlewares.use((req, res, next) => { // traitement personnalisé de la demande... }) } } })
Stockage de l'accès au serveur
Dans certains cas, d'autres hooks de plugins peuvent avoir besoin d'accéder à l'instance du serveur de développement (par exemple, accéder au serveur de socket web, à l'observateur de système de fichiers, ou au graphe de modules). Ce hook peut également être utilisé pour stocker l'instance du serveur afin que d'autres hooks puissent y accéder :
jsconst myPlugin = () => { let server return { nom : 'configure-server', configureServer(_server) { server = _server }, transform(code, id) { if (serveur) { // utilise le serveur... } } } }
Notez que
configureServer
n'est pas appelé lors de l'exécution du build de production, donc vos autres hooks doivent se prémunir contre son absence.
configurePreviewServer
Type:
(server : { middlewares : Connect.Server, httpServer : http.Server }) => (() => void) | void | Promise<(() => void) | void>
Kind:
async
,sequential
Identique à
configureServer
mais pour le serveur de prévisualisation. Il fournit le serveur connect et son [serveur http] sous-jacent(https://nodejs.org/api/http.html). Comme pourconfigureServer
, le hookconfigurePreviewServer
est appelé avant que les autres middlewares ne soient installés. Si vous voulez injecter un middleware après d'autres middlewares, vous pouvez retourner une fonction deconfigurePreviewServer
, qui sera appelée après l'installation des middlewares internes :jsconst myPlugin = () => ({ nom : 'configure-preview-server', configurePreviewServer(server) { // retourne un post hook qui est appelé après que les autres middlewares soient // installés return () => { server.middlewares.use((req, res, next) => { // traitement personnalisé de la demande... }) } } })
transformIndexHtml
Type:
IndexHtmlTransformHook | { enforce? : 'pre' | 'post', transform : IndexHtmlTransformHook }
Kind:
async
,sequential
Crochet dédié à la transformation des fichiers d'entrée HTML tels que
index.html
. Le hook reçoit la chaîne HTML courante et un contexte de transformation. Le contexte expose l'instance deViteDevServer
pendant le développement, et expose le paquet de sortie Rollup pendant la construction.Le hook peut être asynchrone et peut retourner l'un des éléments suivants :
- Chaîne HTML transformée
- Un tableau d'objets descripteurs de tags (
{ tag, attrs, children }
) à injecter dans le HTML existant. Chaque balise peut également spécifier où elle doit être injectée (par défaut, elle est placée en amont de<head>
). - Un objet contenant les deux comme
{ html, tags }
Exemple de base:
jsconst htmlPlugin = () => { return { nom : 'html-transform', transformIndexHtml(html) { return html.replace( /<titre>(.* ?)<\/titre>/, `<titre>Titre remplacé!</titre>` ) } } }
Signature complète du crochet:
` ```ts type IndexHtmlTransformHook = ( html : string, ctx : { path : string filename : string serveur ? ViteDevServer bundle? : import('rollup').OutputBundle chunk? : import('rollup').OutputChunk } ) => | IndexHtmlTransformResult | void | Promise<IndexHtmlTransformResult | void>
type IndexHtmlTransformResult = | Chaîne de caractères | HtmlTagDescriptor[] | { html : string tags : HtmlTagDescriptor[] }
interface HtmlTagDescriptor { tag : chaîne de caractères attrs ? Record<string, string | boolean> children? : string | HtmlTagDescriptor[] /**
- par défaut : 'head-prepend'. */ injectTo ? 'head' | 'body' | 'head-prepend' | 'body-prepend'. }
handleHotUpdate
Type:
(ctx : HmrContext) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>
Effectue une gestion personnalisée des mises à jour HMR. Le hook reçoit un objet de contexte avec la signature suivante :
tsinterface HmrContext { file : string timestamp : nombre modules : Array<ModuleNode> read : () => string | Promise<string> serveur : ViteDevServer }
modules
est un tableau de modules qui sont affectés par le fichier modifié. C'est un tableau car un seul fichier peut correspondre à plusieurs modules servis (par exemple, les SFC de Vue).read
est une fonction de lecture asynchrone qui retourne le contenu du fichier. Ceci est fourni parce que sur certains systèmes, le callback de changement de fichier peut se déclencher trop rapidement avant que l'éditeur ne finisse de mettre à jour le fichier etfs.readFile
direct retournera un contenu vide. La fonction de lecture fournie normalise ce comportement.
Le hook peut choisir de :
Filtrer et réduire la liste des modules affectés afin que le HMR soit plus précis.
Renvoyer un tableau vide et effectuer une gestion personnalisée complète du HMR en envoyant des événements personnalisés au client :
jshandleHotUpdate({serveur }) { server.ws.send({ type : 'custom', événement : 'special-update', données : {} }) return [] }
Le code client doit enregistrer le gestionnaire correspondant à l'aide de l'API HMR (il peut être injecté par le hook
transform
du même plugin) :jsif (import.meta.hot) { import.meta.hot.on('special-update', (data) => { // effectue une mise à jour personnalisée }) }
Ordre des plugins
Un plugin Vite peut en plus spécifier une propriété enforce
(similaire aux chargeurs webpack) pour ajuster l'ordre de ses applications. La valeur de enforce
peut être soit "pre"
soit "post"
. Les plugins résolus seront dans l'ordre suivant :
- Alias
- Plugins utilisateur avec
enforce : 'pre'
. - Plugins de base Vite
- Plugins utilisateur sans valeur enforce
- Plugins de construction Vite
- Greffons utilisateurs avec
enforce : 'post'
. - Plugins Vite post build (minify, manifest, reporting)
Application conditionnelle
Par défaut, les plugins sont invoqués à la fois pour serve et build. Dans les cas où un plugin doit être appliqué de manière conditionnelle uniquement pendant le service ou la construction, utilisez la propriété apply
pour ne les invoquer que pendant 'build'
ou 'serve'
:
js
function myPlugin() {
return {
name : 'build-only',
apply : 'build' // ou 'serve'.
}
}
Une fonction peut également être utilisée pour un contrôle plus précis :
js
apply(config, { command }) {
// appliquer uniquement sur le build mais pas pour le SSR
return command === 'build' && !config.build.ssr
}
Compatibilité avec le plugin Rollup
Un bon nombre de plugins Rollup fonctionneront directement en tant que plugins Vite (par exemple @rollup/plugin-alias
ou @rollup/plugin-json
), mais pas tous, car certains hooks de plugins n'ont pas de sens dans un contexte de serveur de développement dégroupé.
En général, tant qu'un plugin Rollup répond aux critères suivants, il devrait fonctionner comme un plugin Vite :
- Il n'utilise pas le hook
moduleParsed
. - Il n'a pas de couplage fort entre les hooks de la phase bundle et ceux de la phase output.
Si un plugin Rollup n'a de sens que pour la phase de construction, alors il peut être spécifié sous build.rollupOptions.plugins
à la place. Il fonctionnera de la même manière qu'un plugin Vite avec enforce : 'post'
et apply : 'build'
.
Vous pouvez également ajouter à un plugin Rollup existant des propriétés propres à Vite :
js
// vite.config.js
import l'exemple from 'rollup-plugin-example'.
import { defineConfig } from 'vite'.
export default defineConfig({
plugins : [
{
...exemple(),
enforce : 'post',
apply : " build ".
}
]
})
Consultez Vite Rollup Plugins pour obtenir une liste des plugins Rollup officiels compatibles avec les instructions d'utilisation.
Normalisation des chemins
Vite normalise les chemins lors de la résolution des identifiants pour utiliser les séparateurs POSIX ( / ) tout en préservant le volume sous Windows. D'un autre côté, Rollup garde les chemins résolus intacts par défaut, donc les identifiants résolus ont des séparateurs win32 ( / ) dans Windows. Cependant, les plugins Rollup utilisent une fonction utilitaire normalizePath
de @rollup/pluginutils
en interne, qui convertit les séparateurs en POSIX avant d'effectuer les comparaisons. Cela signifie que lorsque ces plugins sont utilisés dans Vite, les motifs de configuration include
et exclude
et d'autres comparaisons similaires de chemins contre des ids résolus fonctionnent correctement.
Ainsi, pour les plugins Vite, lors de la comparaison des chemins contre les ids résolus, il est important de normaliser d'abord les chemins pour utiliser les séparateurs POSIX. Une fonction utilitaire équivalente normalizePath
est exportée par le module vite
.
js
import { normalizePath } from 'vite'.
normalizePath('foo\\\bar') // 'foo/bar'.
normalizePath('foo/bar') // 'foo/bar'.
Filtrage, motif include/exclude
Vite expose la fonction @rollup/pluginutils
's createFilter
pour encourager les plugins et intégrations spécifiques à Vite à utiliser le modèle de filtrage standard include/exclude, qui est également utilisé dans le noyau de Vite lui-même.
Communication client-serveur
Depuis Vite 2.9, nous fournissons quelques utilitaires aux plugins pour les aider à gérer la communication avec les clients.
Communication entre le serveur et le client
Du côté du plugin, nous pouvons utiliser server.ws.send
pour diffuser des événements à tous les clients :
js
// vite.config.js
export default defineConfig({
plugins : [
{
// ...
configureServer(server) {
server.ws.send('my:greetings', { msg : 'hello' })
}
}
]
})
TIP
Nous recommandons de toujours préfixer les noms de vos événements pour éviter les collisions avec d'autres plugins.
Du côté client, utilisez hot.on
pour écouter les événements :
``s // côté client if (i
### Client vers serveur
Pour envoyer des événements du client au serveur, nous pouvons utiliser [`hot.send`](/guide/api-hmr.html#hot-send-event-payload) :
```ts
// côté client
if (import.meta.hot) {
import.meta.hot.send('my:from-client', { msg : 'Hey!' })
}
Utilisez ensuite server.ws.on
pour écouter les événements du côté du serveur :
js
// vite.config.js
export default defineConfig({
plugins : [
{
// ...
configureServer(server) {
server.ws.on('my:from-client', (data, client) => {
console.log('Message from client:', data.msg) // Hey !
// Réponse uniquement au client (si nécessaire)
client.send('my:ack', { msg : 'Hi ! I got your message!' })
})
}
}
]
})
TypeScript pour les événements personnalisés
Il est possible de typer des événements personnalisés en étendant l'interface CustomEventMap
:
``s // events.d.ts Importez 'vite/types/customEvent'.
declare module 'vite/types/customEvent' { interface CustomEventMap { custom:foo' : { msg : string } // 'event-key' : charge utile } }