perf: optimize build — chunk splitting, remove VueDevTools from prod, fix firebase-tools to devDeps
- vite.config.ts: Remove VueDevTools from production build (dev only) - vite.config.ts: Add manualChunks for firebase, charts, pdf, vue, maps vendors - vite.config.ts: Increase chunkSizeWarningLimit to 700KB for Google Maps - package.json: Move firebase-tools from dependencies to devDependencies - router/index.ts: Add webpackChunkName groups (transport, discover, user, admin, roles) - Clean up build log files (build_debug.txt, build_error*.txt, etc.) Build time improvement: chunks now load on demand per user role
This commit is contained in:
@ -18,7 +18,6 @@
|
|||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
"firebase": "^12.9.0",
|
"firebase": "^12.9.0",
|
||||||
"firebase-tools": "^15.7.0",
|
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"jspdf": "^4.1.0",
|
"jspdf": "^4.1.0",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
@ -33,6 +32,7 @@
|
|||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"@vue/tsconfig": "^0.8.1",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
"autoprefixer": "^10.4.24",
|
"autoprefixer": "^10.4.24",
|
||||||
|
"firebase-tools": "^15.7.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"tailwindcss": "^4.2.0",
|
"tailwindcss": "^4.2.0",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~5.9.3",
|
||||||
|
|||||||
@ -4,131 +4,142 @@ import { createRouter, createWebHistory } from 'vue-router'
|
|||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
|
// ─── Vistas Públicas Core (cargadas en el chunk principal) ───────────
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'splash',
|
name: 'splash',
|
||||||
component: () => import('@/views/SplashScreen.vue'),
|
component: () => import(/* webpackChunkName: "splash" */ '@/views/SplashScreen.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/map',
|
path: '/map',
|
||||||
name: 'map',
|
name: 'map',
|
||||||
component: () => import('@/views/MapView.vue'),
|
component: () => import(/* webpackChunkName: "map" */ '@/views/MapView.vue'),
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/discover',
|
|
||||||
name: 'discover',
|
|
||||||
component: () => import('@/views/DiscoverView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/business/:id',
|
|
||||||
name: 'business-details',
|
|
||||||
component: () => import('@/views/BusinessDetailsView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/routes',
|
|
||||||
name: 'routes',
|
|
||||||
component: () => import('@/views/RoutesView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/schedules',
|
|
||||||
name: 'schedules',
|
|
||||||
component: () => import('@/views/SchedulesView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/coupons',
|
|
||||||
name: 'coupons',
|
|
||||||
component: () => import('@/views/CouponsView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/favorites',
|
|
||||||
name: 'favorites',
|
|
||||||
component: () => import('@/views/FavoritesView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/profile',
|
|
||||||
name: 'profile',
|
|
||||||
component: () => import('@/views/ProfileView.vue'),
|
|
||||||
meta: { requiresAuth: true }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/taxi',
|
|
||||||
name: 'taxi',
|
|
||||||
component: () => import('@/views/TaxiView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/bus-stop/:id',
|
|
||||||
name: 'bus-stop-details',
|
|
||||||
component: () => import('@/views/BusStopDetailsView.vue'),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'auth',
|
name: 'auth',
|
||||||
component: () => import('@/views/AuthView.vue'),
|
component: () => import(/* webpackChunkName: "auth" */ '@/views/AuthView.vue'),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ─── Vistas de Transporte (chunk: transport) ─────────────────────────
|
||||||
|
{
|
||||||
|
path: '/routes',
|
||||||
|
name: 'routes',
|
||||||
|
component: () => import(/* webpackChunkName: "transport" */ '@/views/RoutesView.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/schedules',
|
||||||
|
name: 'schedules',
|
||||||
|
component: () => import(/* webpackChunkName: "transport" */ '@/views/SchedulesView.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/bus-stop/:id',
|
||||||
|
name: 'bus-stop-details',
|
||||||
|
component: () => import(/* webpackChunkName: "transport" */ '@/views/BusStopDetailsView.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/taxi',
|
||||||
|
name: 'taxi',
|
||||||
|
component: () => import(/* webpackChunkName: "transport" */ '@/views/TaxiView.vue'),
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Vistas de Descubrir (chunk: discover) ───────────────────────────
|
||||||
|
{
|
||||||
|
path: '/discover',
|
||||||
|
name: 'discover',
|
||||||
|
component: () => import(/* webpackChunkName: "discover" */ '@/views/DiscoverView.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/business/:id',
|
||||||
|
name: 'business-details',
|
||||||
|
component: () => import(/* webpackChunkName: "discover" */ '@/views/BusinessDetailsView.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/coupons',
|
||||||
|
name: 'coupons',
|
||||||
|
component: () => import(/* webpackChunkName: "discover" */ '@/views/CouponsView.vue'),
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Vistas de Usuario (chunk: user) ─────────────────────────────────
|
||||||
|
{
|
||||||
|
path: '/favorites',
|
||||||
|
name: 'favorites',
|
||||||
|
component: () => import(/* webpackChunkName: "user" */ '@/views/FavoritesView.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/profile',
|
||||||
|
name: 'profile',
|
||||||
|
component: () => import(/* webpackChunkName: "user" */ '@/views/ProfileView.vue'),
|
||||||
|
meta: { requiresAuth: true }
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Vistas de Admin (chunk: admin — solo carga para admins) ─────────
|
||||||
{
|
{
|
||||||
path: '/admin',
|
path: '/admin',
|
||||||
name: 'admin-panel',
|
name: 'admin-panel',
|
||||||
component: () => import('@/views/AdminPanel.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminPanel.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/bus-stops',
|
path: '/admin/bus-stops',
|
||||||
name: 'admin-bus-stops',
|
name: 'admin-bus-stops',
|
||||||
component: () => import('@/views/AdminBusStops.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminBusStops.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/routes',
|
path: '/admin/routes',
|
||||||
name: 'admin-routes',
|
name: 'admin-routes',
|
||||||
component: () => import('@/views/AdminRoutes.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminRoutes.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/reports',
|
path: '/admin/reports',
|
||||||
name: 'admin-reports',
|
name: 'admin-reports',
|
||||||
component: () => import('@/views/AdminReports.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminReports.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/schedules',
|
path: '/admin/schedules',
|
||||||
name: 'admin-schedules',
|
name: 'admin-schedules',
|
||||||
component: () => import('@/views/AdminSchedules.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminSchedules.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/drivers',
|
path: '/admin/drivers',
|
||||||
name: 'admin-drivers',
|
name: 'admin-drivers',
|
||||||
component: () => import('@/views/AdminDrivers.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminDrivers.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/analytics',
|
path: '/admin/analytics',
|
||||||
name: 'admin-analytics',
|
name: 'admin-analytics',
|
||||||
component: () => import('@/views/StrategicAnalytics.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/StrategicAnalytics.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/taxis',
|
path: '/admin/taxis',
|
||||||
name: 'admin-taxis',
|
name: 'admin-taxis',
|
||||||
component: () => import('@/views/AdminTaxis.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminTaxis.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/shuttles',
|
path: '/admin/shuttles',
|
||||||
name: 'admin-shuttles',
|
name: 'admin-shuttles',
|
||||||
component: () => import('@/views/AdminShuttles.vue'),
|
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminShuttles.vue'),
|
||||||
meta: { requiresAuth: true, role: 'admin' }
|
meta: { requiresAuth: true, role: 'admin' }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ─── Vistas de Roles Especiales (chunk: roles) ───────────────────────
|
||||||
{
|
{
|
||||||
path: '/promoter',
|
path: '/promoter',
|
||||||
name: 'promoter-dashboard',
|
name: 'promoter-dashboard',
|
||||||
component: () => import('@/views/PromoterDashboard.vue'),
|
component: () => import(/* webpackChunkName: "roles" */ '@/views/PromoterDashboard.vue'),
|
||||||
meta: { requiresAuth: true, role: ['PROMOTER', 'ADMIN'] }
|
meta: { requiresAuth: true, role: ['PROMOTER', 'ADMIN'] }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/driver',
|
path: '/driver',
|
||||||
name: 'driver-dashboard',
|
name: 'driver-dashboard',
|
||||||
component: () => import('@/views/DriverDashboard.vue'),
|
component: () => import(/* webpackChunkName: "roles" */ '@/views/DriverDashboard.vue'),
|
||||||
meta: { requiresAuth: true, role: ['DRIVER', 'ADMIN'] }
|
meta: { requiresAuth: true, role: ['DRIVER', 'ADMIN'] }
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -158,4 +169,3 @@ router.beforeEach((to, _from, next) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
||||||
|
|||||||
@ -1,89 +1,128 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import VueDevTools from 'vite-plugin-vue-devtools'
|
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig(({ mode }) => {
|
||||||
plugins: [
|
const isDev = mode === 'development'
|
||||||
vue(),
|
|
||||||
tailwindcss(),
|
return {
|
||||||
VueDevTools(),
|
plugins: [
|
||||||
VitePWA({
|
vue(),
|
||||||
registerType: 'autoUpdate',
|
tailwindcss(),
|
||||||
includeAssets: ['icon-192.png', 'icon-512.png', 'icon-1024.png', 'favicon.ico'],
|
// VueDevTools SOLO en desarrollo — no se incluye en el bundle de producción
|
||||||
manifest: {
|
...(isDev ? [require('vite-plugin-vue-devtools').default()] : []),
|
||||||
name: 'SIBU - Sistema de Transporte',
|
VitePWA({
|
||||||
short_name: 'SIBU',
|
registerType: 'autoUpdate',
|
||||||
description: 'Sistema de Transporte Público',
|
includeAssets: ['icon-192.png', 'icon-512.png', 'icon-1024.png', 'favicon.ico'],
|
||||||
theme_color: '#fee715',
|
manifest: {
|
||||||
background_color: '#ffffff',
|
name: 'SIBU - Sistema de Transporte',
|
||||||
display: 'standalone',
|
short_name: 'SIBU',
|
||||||
orientation: 'portrait',
|
description: 'Sistema de Transporte Público',
|
||||||
scope: '/',
|
theme_color: '#fee715',
|
||||||
start_url: '/',
|
background_color: '#ffffff',
|
||||||
icons: [
|
display: 'standalone',
|
||||||
{
|
orientation: 'portrait',
|
||||||
src: 'icon-192.png',
|
scope: '/',
|
||||||
sizes: '192x192',
|
start_url: '/',
|
||||||
type: 'image/png',
|
icons: [
|
||||||
},
|
{
|
||||||
{
|
src: 'icon-192.png',
|
||||||
src: 'icon-512.png',
|
sizes: '192x192',
|
||||||
sizes: '512x512',
|
type: 'image/png',
|
||||||
type: 'image/png',
|
},
|
||||||
},
|
{
|
||||||
{
|
src: 'icon-512.png',
|
||||||
src: 'icon-1024.png',
|
sizes: '512x512',
|
||||||
sizes: '1024x1024',
|
type: 'image/png',
|
||||||
type: 'image/png',
|
},
|
||||||
},
|
{
|
||||||
],
|
src: 'icon-1024.png',
|
||||||
},
|
sizes: '1024x1024',
|
||||||
workbox: {
|
type: 'image/png',
|
||||||
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
|
},
|
||||||
runtimeCaching: [
|
],
|
||||||
{
|
},
|
||||||
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
|
workbox: {
|
||||||
handler: 'CacheFirst',
|
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
|
||||||
options: {
|
runtimeCaching: [
|
||||||
cacheName: 'google-fonts-cache',
|
{
|
||||||
expiration: {
|
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
|
||||||
maxEntries: 10,
|
handler: 'CacheFirst',
|
||||||
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
|
options: {
|
||||||
},
|
cacheName: 'google-fonts-cache',
|
||||||
cacheableResponse: {
|
expiration: {
|
||||||
statuses: [0, 200],
|
maxEntries: 10,
|
||||||
|
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
|
||||||
|
},
|
||||||
|
cacheableResponse: {
|
||||||
|
statuses: [0, 200],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
|
||||||
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
|
handler: 'CacheFirst',
|
||||||
handler: 'CacheFirst',
|
options: {
|
||||||
options: {
|
cacheName: 'gstatic-fonts-cache',
|
||||||
cacheName: 'gstatic-fonts-cache',
|
expiration: {
|
||||||
expiration: {
|
maxEntries: 10,
|
||||||
maxEntries: 10,
|
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
|
||||||
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
|
},
|
||||||
},
|
cacheableResponse: {
|
||||||
cacheableResponse: {
|
statuses: [0, 200],
|
||||||
statuses: [0, 200],
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
],
|
},
|
||||||
|
devOptions: {
|
||||||
|
enabled: false,
|
||||||
|
type: 'module',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src'),
|
||||||
},
|
},
|
||||||
devOptions: {
|
|
||||||
enabled: false,
|
|
||||||
type: 'module',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': path.resolve(__dirname, './src'),
|
|
||||||
},
|
},
|
||||||
},
|
build: {
|
||||||
|
// Ajustar límite de advertencia (MapView + Google Maps son grandes por naturaleza)
|
||||||
|
chunkSizeWarningLimit: 700,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
// Dividir código en chunks lógicos para cargas más rápidas en producción
|
||||||
|
manualChunks: (id) => {
|
||||||
|
// Vendor: Firebase (bundle más pesado del stack)
|
||||||
|
if (id.includes('node_modules/firebase')) {
|
||||||
|
return 'vendor-firebase'
|
||||||
|
}
|
||||||
|
// Vendor: Charts y PDF (solo usados en vistas de Admin)
|
||||||
|
if (id.includes('node_modules/chart.js') || id.includes('node_modules/vue-chartjs')) {
|
||||||
|
return 'vendor-charts'
|
||||||
|
}
|
||||||
|
if (id.includes('node_modules/jspdf') || id.includes('node_modules/html2canvas')) {
|
||||||
|
return 'vendor-pdf'
|
||||||
|
}
|
||||||
|
// Vendor: Ecosistema Vue (vue, pinia, router, i18n)
|
||||||
|
if (
|
||||||
|
id.includes('node_modules/vue') ||
|
||||||
|
id.includes('node_modules/pinia') ||
|
||||||
|
id.includes('node_modules/vue-router') ||
|
||||||
|
id.includes('node_modules/vue-i18n')
|
||||||
|
) {
|
||||||
|
return 'vendor-vue'
|
||||||
|
}
|
||||||
|
// Vendor: Google Maps loader
|
||||||
|
if (id.includes('node_modules/@googlemaps')) {
|
||||||
|
return 'vendor-maps'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user