Gerenciando Múltiplos Projetos com Saídas Distintas em Vue 3 e Vite

No desenvolvimento front-end moderno, a combinação de Vue 3 com Vite oferece um ambiente de desnevolvimento ágil e performático. Projetos frequentemente evoluem para abranger múltiplos subsistemas que requerem compilações separadas (dist). Este guia detalha como configurar um ambiente Vue 3 com Vite para suportar múltiplos projetos, garantindo a geração de múltiplos pacotes de distribuição.

Configurando a Estrutura do Projeto Principal

Inicie um novo projeto Vue 3 utilizando o Vite:

npm create vue@latest my-monorepo

Estrutura de Diretórios dos Subsistemas

Organize seus subsistemas dentro do diretório src do projeto principal. Por exemplo:

my-monorepo/
├── public/
├── src/
│   ├── assets/
│   ├── components/
│   ├── projects/
│   │   ├── projectA/
│   │   │   ├── App.vue
│   │   │   └── main.js
│   │   ├── projectB/
│   │   │   ├── App.vue
│   │   │   └── main.js
│   │   └── router/
│   │       └── index.js
│   ├── App.vue
│   ├── main.js
│   └── index.html
├── index.html
├── vite.config.js
├── package.json
├── projectConfig.js
└── dynamic-build-all.js

Arquivos Específicos dos Subsistemas

Cada subsistema necessitará de seus próprios App.vue e main.js. Adapte os caminhos de recursos e importações conforme necessário. Por exemplo, em src/projects/projectA/App.vue:

<script setup>
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from '../../components/HelloWorld.vue' // Ajuste o caminho
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="../../assets/logo.svg" width="125" height="125" />

    <div class="wrapper">
      <HelloWorld msg="Bem-vindo ao Projeto A!" />

      <nav>
        <RouterLink to="/">Home</RouterLink>
        <RouterLink to="/about">Sobre</RouterLink>
      </nav>
    </div>
  </header>

  <RouterView />
</template>

<style scoped>
/* Estilos aqui */
</style>

E em src/projects/projectA/main.js:

import '../../assets/main.css' // Ajuste o caminho

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from '../../router' // Ajuste o caminho

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

Para cada subsistema, crie um arquivo HTML correspondente (ou reutilize o index.html principal se os títulos e ícones forem idênticos). Por exemplo, projectA.html:


<html lang="pt-br">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Projeto A</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/projects/projectA/main.js"></script>
  </body>
</html>

Configurando Múltiplas Saídas (Dist)

Crie um arquivo projectConfig.js para centralizar as configurações de entrada de cada subsistema:

export const projectConfigs = {
    projectA: {
      minify: true,
      pages: [
          {
              filename: 'projecta', // Nome do arquivo HTML de saída
              entry: 'src/projects/projectA/main.js', // Ponto de entrada do script
              template: 'projectA.html', // Arquivo HTML de modelo
          },
      ],
    },
    projectB: {
      minify: true,
      pages: [
          {
              filename: 'projectb',
              entry: 'src/projects/projectB/main.js',
              template: 'projectB.html',
          },
      ],
    }
  }

Modifique seu arquivo vite.config.js para incorporar essas configurações:

import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { createHtmlPlugin } from 'vite-plugin-html'
import { resolve } from 'node:path'
import { projectConfigs } from './projectConfig.js'

// Obtém o nome do projeto através de variáveis de ambiente
const projectName = process.env.PROJECT_NAME

// Verifica se o nome do projeto foi fornecido
if (!projectName || !projectConfigs[projectName]) {
  console.error('Erro: PROJECT_NAME não definido ou inválido. Use um dos seguintes: ' + Object.keys(projectConfigs).join(', '));
  process.exit(1);
}

const currentProjectConfig = projectConfigs[projectName];

export default defineConfig({
    base: './',
    publicDir: 'public',
    plugins: [
        vue(),
        createHtmlPlugin({
          // Configura o plugin para usar as páginas do projeto atual
          entry: currentProjectConfig.pages[0].entry, // Define o ponto de entrada principal para o plugin HTML
          template: currentProjectConfig.pages[0].template, // Define o template principal
          // Você pode precisar ajustar a lógica do createHtmlPlugin para lidar com múltiplos 'pages' explicitamente
          // se ele não suportar nativamente em sua configuração atual.
          // Uma abordagem comum é gerar cada página separadamente.
          // Para simplicidade, este exemplo assume que o 'entry' e 'template' são para a configuração principal.
        })
    ],
    resolve: {
      alias: {
        '@': resolve(__dirname, 'src')
      }
    },
    build: {
        cssCodeSplit: true,
        emptyOutDir: true,
        sourcemap: false,
        assetsDir: 'assets',
        // O diretório de saída agora é dinâmico
        outDir: `dist-${projectName}`,
        rollupOptions: {
            input: {
                // Mapeia os pontos de entrada para cada projeto
                // Isso é crucial para o Vite entender quais arquivos compilar
                ...currentProjectConfig.pages.reduce((acc, page) => {
                  acc[page.filename] = page.entry;
                  return acc;
                }, {}),
            },
            output: {
                compact: true,
                entryFileNames: "assets/js/[name]-[hash].js",
                chunkFileNames: "assets/js/[name]-[hash].js",
                assetFileNames: "assets/[ext]/[name].[ext]",
                // Garante que cada entrada tenha sua própria saída HTML se using build.rollupOptions.input for multiple entries
                // Para múltiplas páginas, pode ser necessário um plugin adicional ou configuração mais complexa
                // A configuração 'pages' do createHtmlPlugin é mais adequada para este cenário.
            }
        }
    },
})

Atualize o script build no seu package.json:

"scripts": {
  "dev": "vite",
  "build": "node ./scripts/build-all.js",
  "preview": "vite preview"
},

Crie um script para gerenciar as compilações de múltiplos projetos, por exemplo, scripts/build-all.js:

import { execSync } from 'child_process';
import { projectConfigs } from '../projectConfig.js';

const projectNames = Object.keys(projectConfigs);

projectNames.forEach(projectName => {
  console.log(`\nIniciando a compilação para o projeto: ${projectName}...`);
  try {
    // Define a variável de ambiente PROJECT_NAME e executa o comando de build do Vite
    execSync(`cross-env PROJECT_NAME=${projectName} vite build`, { stdio: 'inherit' });
    console.log(`Compilação do projeto ${projectName} concluída com sucesso.`);
  } catch (error) {
    console.error(`Erro durante a compilação do projeto ${projectName}:`, error);
    process.exit(1); // Sai com erro se um projeto falhar
  }
});

console.log('\nTodos os projetos foram compilados.');

O arquivo projectConfig.js agora é a fonte única de verdade para os projetos a serem compilados. Não é mais necessário um arquivo separado como projects.js.

Executando a Compilação

Execute o script de compilação para gerar os pacotes individuais:

npm run build

Após a execução, você encontrará diretórios dist-projectA, dist-projectB, etc., contendo os pacotes compilados para cada subsistema.

Tags: vuejs Vite monorepo build-config multiple-outputs

Publicado em 6-10 03:57 por Thomas