Vite se está volviendo cada vez más popular entre los desarrolladores, pero como la comunidad no es tan grande (como en Webpack ), es posible que necesites crear tu propio complemento personalizado para resolver tus problemas. En este artículo, discutiremos cómo crear un complemento para Vite y desglosaré mi propio complemento.
Para crear un complemento, es importante saber que Vite utiliza diferentes sistemas de compilación para el servidor de desarrollo (comando vite
) y el paquete (comando vite build
).
Para el servidor de desarrollo, utiliza esbuild con módulos ES nativos, que son compatibles con los navegadores modernos, y no necesitamos agrupar el código en un solo archivo, y nos brinda HRM (Reemplazo de módulo en caliente) rápido.
Para el paquete, utiliza rollup.js porque es flexible y tiene un ecosistema grande; permite la creación de paquetes de producción altamente optimizados con diferentes formatos de salida.
La interfaz del complemento de Vite se basa en la de Rollup pero con opciones y enlaces adicionales para trabajar con el servidor de desarrollo.
Cuando crea un complemento, puede integrarlo en su vite.config.js
. No es necesario crear un nuevo paquete para ello. Una vez que vea que un complemento ha sido útil en sus proyectos, considere compartirlo con la comunidad y contribuir al ecosistema de Vite.
Además, dado que rollup.js tiene una comunidad y un ecosistema más grandes, podría considerar crear un complemento para rollup.js, y funcionará igual de bien en Vite. Por lo tanto, si la funcionalidad de su complemento funciona solo para el paquete, puede utilizar el complemento rollup.js en lugar de Vite, y los usuarios pueden usar su complemento acumulativo en sus proyectos de Vite sin ningún problema.
Si crea un complemento para el resumen, cubrirá a más usuarios que usan solo rollup.js. Si su complemento afectará al servidor de desarrollo, entonces elija el complemento Vite.
Comencemos a crear un complemento directamente en 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(), ], });
En este ejemplo, he creado un complemento llamado myPlugin
que imprime la configuración de Vite tan pronto como se resuelve en la consola en ambas etapas: servidor de desarrollo y paquete. Si quiero imprimir la configuración solo en el modo de servidor de desarrollo, entonces debo agregar apply: 'serve'
y apply: 'build'
para el paquete.
// 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(), ], });
Además, puedo devolver una variedad de complementos; Es útil para separar la funcionalidad del servidor de desarrollo y del paquete:
// 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(), ], });
Y eso es prácticamente todo; puedes agregar fácilmente pequeños complementos a la configuración de Vite. Y si un complemento se vuelve demasiado grande, prefiero moverlo a otro archivo o incluso crear un paquete.
Si necesita algo más complejo, hay muchos enlaces útiles que puede explorar en la documentación de Vite. Pero como ejemplo, analicemos mi propio complemento a continuación.
Entonces, tengo un complemento para crear sprites SVG basados en archivos de íconos: vite-plugin-svg-spritemap .
El objetivo es capturar todos los íconos .svg
en la carpeta src/icons
y recopilar su contenido en un único archivo .svg
que se llama sprite SVG. Comencemos desde la etapa del paquete:
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
nos permite obtener la configuración cuando esté resuelta para usarla en los siguientes ganchos;
El gancho writeBundle
se llama una vez finalizado el proceso de agrupación, y aquí crearé el archivo sprite.svg
;
La función getSpriteContent
devuelve una cadena de sprites SVG preparados según el patrón src/icons/*.svg
. No profundizaré con este; puedes consultar mi otro artículo que explica todo el proceso de generación de sprites SVG ;
Luego, creo la ruta absoluta al sprite.svg
para colocar el contenido del sprite SVG con path.resolve()
, me aseguro de que el archivo exista (o creo uno) con fs.ensureFileSync
., y escribo el contenido del sprite SVG en él .
Ahora, venga la parte más interesante: la etapa del servidor de desarrollo. No puedo usar writeBundle
aquí y no puedo alojar archivos cuando el servidor de desarrollo se está ejecutando, por lo que necesitamos usar middleware del servidor para capturar la solicitud a 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
es un enlace para configurar el servidor de desarrollo. Se activa antes de que se instale el middleware interno de Vite; en mi caso, necesito agregar middleware personalizado después del middleware interno, por lo que devuelvo una función;
Para agregar middleware personalizado para capturar cada solicitud al servidor de desarrollo, uso server.middlewares.use()
. Lo necesito para detectar solicitudes con la URL [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg)
, para poder emular el comportamiento del archivo;
Si la URL de solicitud no es /sprite.svg
, pase al siguiente middleware (es decir, pase el control al siguiente controlador de la cadena);
Para preparar el contenido del archivo, coloco el resultado de getSpriteContent
en la variable sprite
y lo envío como respuesta con encabezados configurados (tipo de contenido y estado HTTP 200).
Como resultado, simulé el comportamiento del archivo.
Pero si se cambian, eliminan o agregan archivos en src/icons
, debemos reiniciar el servidor para generar nuevo contenido de sprites a través de getSpriteContent
; Para esto, usaré la biblioteca de observación de archivos: chokidar . Agreguemos controladores chokidar al código:
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); }); }; }, }; }
Como puedes ver, la API de creación de complementos no es realmente complicada. Sólo necesita encontrar ganchos de Vite o Rollup que se adapten a sus tareas. En mi ejemplo, estoy usando writeBundle
de Rollup.js (como dije, se usa para generar el paquete) y configureServer
de Vite porque Rollup.js no tiene soporte de servidor de desarrollo nativo.
En el caso de writeBundle
fue muy simple, tomamos el contenido del sprite SVG y lo colocamos en un archivo. En el caso del servidor de desarrollo, me confundió por qué no podía hacer lo mismo; Miré los complementos de otros autores y todos hacen más o menos lo mismo.
Entonces, uso configureServer
y, a través del argumento server
, agrego middleware que activa cada solicitud al servidor de desarrollo al interceptar la solicitud sprite.svg
.
Como mencioné antes, para crear complementos más útiles, debes explorar los enlaces. Se explican detalladamente en la documentación:
https://vitejs.dev/guide/api-plugin#universal-hooks
https://vitejs.dev/guide/api-plugin#vite-specific-hooks
En términos de nombres, Vite tiene algunas convenciones para complementos, por lo que será mejor que lo revises antes de terminar. Aquí hay algunos puntos clave:
vite-plugin-
;
vite-plugin
en package.json;
vite-plugin-vue-
, vite-plugin-react-
, vite-plugin-svelte-
).Si decide publicar su complemento en NPM, lo recomiendo porque compartir conocimientos y experiencia es un principio fundamental de la comunidad de TI, fomentando el crecimiento colectivo. Para aprender cómo publicar y mantener su paquete, consulte mi guía → La forma más sencilla de crear un paquete NPM .
También recomiendo enviar su complemento a la lista de la comunidad de vite: awesome-vite . Mucha gente busca allí los complementos más adecuados y sería una gran oportunidad para contribuir al ecosistema de Vite. El proceso de enviar su complemento allí es simple: solo asegúrese de cumplir con los términos y crear una solicitud de extracción. Puede encontrar la lista de términos aquí .
En general, debe ser específico de Vite (no un resumen), de código abierto y tener buena documentación. ¡Buena suerte con tus complementos!