O Vite está se tornando cada vez mais popular entre os desenvolvedores, mas como a comunidade não é tão grande (como no Webpack ), pode ser necessário criar seu próprio plugin personalizado para resolver seus problemas. Neste artigo, discutiremos como criar um plugin para Vite e detalharei meu próprio plugin.
Para criar um plugin, é importante saber que o Vite utiliza diferentes sistemas de build para o servidor de desenvolvimento (comando vite
) e o bundle (comando vite build
).
Para o servidor de desenvolvimento, ele usa esbuild com módulos ES nativos, que são suportados por navegadores modernos, e não precisamos agrupar o código em um único arquivo, e isso nos fornece HRM (Hot Module Replacement) rápido.
Para o pacote, ele usa rollup.js porque é flexível e possui um grande ecossistema; permite a criação de pacotes de produção altamente otimizados com diferentes formatos de saída.
A interface do plugin do Vite é baseada no Rollup, mas com opções e ganchos adicionais para trabalhar com o servidor de desenvolvimento.
Ao criar um plug-in, você pode incorporá-lo em seu vite.config.js
. Não há necessidade de criar um novo pacote para isso. Depois de perceber que um plugin foi útil em seus projetos, considere compartilhá-lo com a comunidade e contribuir com o ecossistema Vite.
Além disso, como rollup.js tem uma comunidade e um ecossistema maiores, você pode considerar a criação de um plugin para rollup.js, e ele funcionará perfeitamente no Vite. Portanto, se a funcionalidade do seu plugin funcionar apenas para o pacote, você pode usar o plugin rollup.js em vez do Vite, e os usuários podem usar o plugin rollup em seus projetos Vite sem problemas.
Se você criar um plug-in para rollup, abrangerá mais usuários que usam apenas rollup.js. Se o seu plugin afetar o servidor de desenvolvimento, você deve usar o plugin Vite.
Vamos começar a criar o plugin diretamente em 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(), ], });
Neste exemplo, criei um plugin chamado myPlugin
que imprime a configuração do Vite assim que for resolvido no console em ambos os estágios: dev server e bundle. Se eu quiser imprimir a configuração apenas no modo de servidor de desenvolvimento, devo adicionar apply: 'serve'
e apply: 'build'
para pacote.
// 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(), ], });
Além disso, posso retornar uma série de plugins; é útil para separar funcionalidades do servidor de desenvolvimento e do pacote:
// 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(), ], });
E é basicamente isso; você pode facilmente adicionar pequenos plug-ins à configuração do Vite. E se um plugin ficar muito grande, prefiro movê-lo para outro arquivo ou até mesmo criar um pacote.
Se você precisar de algo mais complexo, há muitos ganchos úteis que você pode explorar na documentação do Vite. Mas, como exemplo, vamos detalhar meu próprio plugin abaixo.
Então, eu tenho um plugin para criar sprites SVG baseados em arquivos de ícones - vite-plugin-svg-spritemap .
O objetivo é pegar todos os ícones .svg
na pasta src/icons
e coletar seu conteúdo em um único arquivo .svg
chamado SVG sprite. Vamos começar pelo estágio de pacote:
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 obter a configuração quando for resolvido para usá-la nos próximos ganchos;
O gancho writeBundle
é chamado após a conclusão do processo de empacotamento e, aqui, criarei o arquivo sprite.svg
;
A função getSpriteContent
retorna uma string de sprites SVG preparados com base no padrão src/icons/*.svg
. Não vou me aprofundar nisso; você pode conferir meu outro artigo explicando todo o processo de geração de sprites SVG ;
Em seguida, eu crio o caminho absoluto para sprite.svg
para colocar o conteúdo do sprite SVG com path.resolve()
, certifico-me de que o arquivo existe (ou crio um) com fs.ensureFileSync
., e gravo o conteúdo do sprite SVG nele .
Agora, a parte mais interessante – o estágio do servidor de desenvolvimento. Não posso usar writeBundle
aqui e não posso hospedar arquivos quando o servidor de desenvolvimento está em execução, então precisamos usar o middleware do servidor para capturar a solicitação para 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
é um gancho para configurar o servidor de desenvolvimento. Ele é acionado antes que o middleware interno do Vite seja instalado; no meu caso, preciso adicionar middleware customizado após o middleware interno, então retorno uma função;
Para adicionar middleware personalizado para capturar todas as solicitações ao servidor de desenvolvimento, eu uso server.middlewares.use()
. Preciso dele para detectar solicitações com a URL [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg)
, para poder emular o comportamento do arquivo;
Se a URL da solicitação não for /sprite.svg
- pule para o próximo middleware (ou seja, passe o controle para o próximo manipulador na cadeia);
Para preparar o conteúdo do arquivo, coloco o resultado de getSpriteContent
na variável sprite
e envio como resposta com cabeçalhos configurados (tipo de conteúdo e status HTTP 200).
Como resultado, simulei o comportamento do arquivo.
Mas se os arquivos em src/icons
forem alterados, excluídos ou adicionados, devemos reiniciar o servidor para gerar novo conteúdo de sprite via getSpriteContent
; para isso, usarei a biblioteca de observação de arquivos - chokidar . Vamos adicionar manipuladores chokidar ao 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 você pode ver, a API de criação de plugins não é muito complicada. Você só precisa encontrar ganchos do Vite ou Rollup que se adaptem às suas tarefas. No meu exemplo, estou usando writeBundle
do Rollup.js (como eu disse, ele é usado para gerar o pacote) e configureServer
do Vite porque Rollup.js não tem suporte nativo ao servidor de desenvolvimento.
No caso do writeBundle
foi muito simples, pegamos o conteúdo do sprite SVG e colocamos em um arquivo. No caso do servidor de desenvolvimento, fiquei confuso porque não pude fazer o mesmo; Eu olhei para plug-ins de outros autores e todos eles fazem a mesma coisa.
Então, eu uso configureServer
e, por meio do argumento server
, adiciono middleware que aciona todas as solicitações ao servidor de desenvolvimento, interceptando a solicitação sprite.svg
.
Como mencionei antes, para criar plugins mais úteis, você precisa explorar os ganchos. Eles são explicados em detalhes na documentação:
https://vitejs.dev/guide/api-plugin#universal-hooks
https://vitejs.dev/guide/api-plugin#vite-specific-hooks
Em termos de nomenclatura, o Vite possui algumas convenções para plugins, então é melhor você conferir antes de terminar. Aqui estão alguns pontos-chave:
vite-plugin-
;
vite-plugin
em package.json;
vite-plugin-vue-
, vite-plugin-react-
, vite-plugin-svelte-
).Se você decidir publicar seu plugin no NPM, recomendo porque compartilhar conhecimento e expertise é um princípio fundamental da comunidade de TI, fomentando o crescimento coletivo. Para saber como publicar e manter seu pacote, confira meu guia → A maneira mais fácil de criar um pacote NPM .
Eu também recomendo enviar seu plugin para a lista da comunidade do vite - awesome-vite . Muitas pessoas procuram os plugins mais adequados por lá, e seria uma ótima oportunidade de contribuir com o ecossistema Vite! O processo de envio do seu plugin é simples - apenas certifique-se de cumprir os termos e criar uma solicitação pull. Você pode encontrar a lista de termos aqui .
No geral, ele precisa ser específico para Vite (não rollup), código aberto e ter boa documentação. Boa sorte com seus plug-ins!