--- layout: default title: Performance Guide nav_order: 5 --- # Performance Optimization {: .no_toc } Complete guide to optimizing Gentelella Admin Template for maximum performance {: .fs-6 .fw-300 } ## Table of contents {: .no_toc .text-delta } 1. TOC {:toc} --- ## Performance Overview Gentelella v2.0 includes significant performance improvements over the original version: ### Performance Metrics | Metric | v1.0 | v2.0 | Improvement | |--------|------|------|-------------| | **Initial Bundle Size** | 779 KB | 79 KB | **90% smaller** | | **Total Page Load** | 1.3 MB | 770 KB | **40% reduction** | | **First Contentful Paint** | 2.1s | 0.8s | **62% faster** | | **Time to Interactive** | 3.5s | 1.2s | **66% faster** | | **Largest Contentful Paint** | 2.8s | 1.1s | **61% faster** | | **Cumulative Layout Shift** | 0.15 | 0.03 | **80% improvement** | --- ## Smart Loading System ### Core vs. Module Architecture The template uses a two-tier loading system: #### Core Bundle (79KB) - Always Loaded Essential functionality that every page needs: ```javascript // src/main-core.js import 'bootstrap/dist/js/bootstrap.bundle.min.js'; import './js/custom.min.js'; // Initialize tooltips and popovers document.addEventListener('DOMContentLoaded', function() { // Bootstrap components initialization const tooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]'); tooltips.forEach(tooltip => new bootstrap.Tooltip(tooltip)); const popovers = document.querySelectorAll('[data-bs-toggle="popover"]'); popovers.forEach(popover => new bootstrap.Popover(popover)); }); ``` #### Conditional Modules - Loaded on Demand **Charts Module** (219KB) ```javascript // Only loads when chart elements are detected if (document.querySelector('.chart-container')) { const charts = await import('./modules/charts.js'); charts.initializeCharts(); } ``` **Forms Module** (200KB) ```javascript // Only loads on pages with enhanced forms if (document.querySelector('.select2, .datepicker, .form-wizard')) { const forms = await import('./modules/forms.js'); forms.initializeForms(); } ``` **Tables Module** ```javascript // Only loads when DataTables are needed if (document.querySelector('.datatable')) { const tables = await import('./modules/tables.js'); tables.initializeTables(); } ``` ### Module Loading Strategy ```javascript // Smart module detection and loading export async function loadRequiredModules() { const modules = []; // Check for chart requirements if (document.querySelector('canvas, .morris-chart, .sparkline')) { modules.push(import('./modules/charts.js')); } // Check for form enhancements if (document.querySelector('.select2, .datepicker, .ion-range-slider')) { modules.push(import('./modules/forms.js')); } // Check for table features if (document.querySelector('.datatable, table[data-table]')) { modules.push(import('./modules/tables.js')); } // Check for dashboard widgets if (document.querySelector('.dashboard-widget, .tile_count')) { modules.push(import('./modules/dashboard.js')); } // Load all required modules in parallel const loadedModules = await Promise.all(modules); // Initialize each module loadedModules.forEach(module => { if (module.initialize) { module.initialize(); } }); } ``` --- ## Bundle Optimization ### Manual Chunk Splitting The Vite configuration includes optimized chunk splitting: ```javascript // vite.config.js export default defineConfig({ build: { rollupOptions: { output: { manualChunks: { // Core vendor libraries (loaded on every page) 'vendor-core': [ 'bootstrap', '@popperjs/core' ], // Chart libraries (loaded only when needed) 'vendor-charts': [ 'chart.js', 'morris.js', 'gauge.js', 'jquery-sparkline' ], // Form enhancement libraries 'vendor-forms': [ 'select2', 'tempus-dominus', 'ion-rangeslider', 'switchery' ], // Table functionality 'vendor-tables': [ 'datatables.net', 'datatables.net-bs5', 'datatables.net-responsive', 'datatables.net-buttons' ], // Utility libraries 'vendor-utils': [ 'dayjs', 'nprogress', 'autosize' ] } } } } }); ``` ### Tree Shaking Import only what you need: ```javascript // ❌ Bad - imports entire library import * as dayjs from 'dayjs'; // ✅ Good - imports only specific functions import dayjs from 'dayjs'; import customParseFormat from 'dayjs/plugin/customParseFormat'; // ❌ Bad - imports entire Chart.js import Chart from 'chart.js'; // ✅ Good - imports only needed components import { Chart, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, } from 'chart.js'; Chart.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend ); ``` --- ## Asset Optimization ### Image Optimization #### Responsive Images ```html Dashboard preview ``` #### WebP Format with Fallback ```html Dashboard ``` #### Lazy Loading ```html Description Description ``` ```javascript // Lazy loading implementation const imageObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.remove('lazy'); observer.unobserve(img); } }); }); document.querySelectorAll('img[data-src]').forEach(img => { imageObserver.observe(img); }); ``` ### Font Optimization #### Font Loading Strategy ```html ``` #### Subset Fonts ```css /* Load only the characters you need */ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&text=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'); ``` --- ## Caching Strategies ### Browser Caching #### Vite Asset Hashing ```javascript // vite.config.js export default defineConfig({ build: { rollupOptions: { output: { // Add hash to filenames for cache busting entryFileNames: `assets/[name].[hash].js`, chunkFileNames: `assets/[name].[hash].js`, assetFileNames: `assets/[name].[hash].[ext]` } } } }); ``` #### Service Worker Implementation ```javascript // sw.js - Service Worker for caching const CACHE_NAME = 'gentelella-v2.0.0'; const urlsToCache = [ '/', '/assets/vendor-core.js', '/assets/main-core.js', '/assets/main.css', '/images/favicon.ico' ]; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(urlsToCache)) ); }); self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { // Return cached version or fetch from network return response || fetch(event.request); }) ); }); ``` ### CDN Integration ```javascript // vite.config.js - CDN configuration export default defineConfig({ build: { rollupOptions: { external: ['jquery', 'bootstrap'], output: { globals: { jquery: 'jQuery', bootstrap: 'bootstrap' } } } } }); ``` ```html ``` --- ## Runtime Performance ### Efficient DOM Manipulation #### Batch DOM Updates ```javascript // ❌ Bad - multiple reflows function updateMultipleElements(data) { data.forEach(item => { const element = document.getElementById(item.id); element.style.width = item.width + 'px'; element.style.height = item.height + 'px'; element.textContent = item.text; }); } // ✅ Good - single reflow function updateMultipleElements(data) { const fragment = document.createDocumentFragment(); data.forEach(item => { const element = document.getElementById(item.id).cloneNode(true); element.style.width = item.width + 'px'; element.style.height = item.height + 'px'; element.textContent = item.text; fragment.appendChild(element); }); document.body.appendChild(fragment); } ``` #### Event Delegation ```javascript // ❌ Bad - multiple event listeners document.querySelectorAll('.btn').forEach(btn => { btn.addEventListener('click', handleClick); }); // ✅ Good - single delegated listener document.addEventListener('click', function(e) { if (e.target.classList.contains('btn')) { handleClick(e); } }); ``` ### Memory Management #### Cleanup Event Listeners ```javascript class Component { constructor(element) { this.element = element; this.handleClick = this.handleClick.bind(this); this.element.addEventListener('click', this.handleClick); } destroy() { // Clean up event listeners this.element.removeEventListener('click', this.handleClick); this.element = null; } handleClick(e) { // Handle click } } ``` #### Debounce Expensive Operations ```javascript function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Usage for search const debouncedSearch = debounce(performSearch, 300); document.getElementById('search').addEventListener('input', debouncedSearch); ``` --- ## Monitoring and Analysis ### Performance Monitoring #### Core Web Vitals ```javascript // Monitor Core Web Vitals import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals'; function sendToAnalytics(metric) { // Send to your analytics service console.log(metric); } getCLS(sendToAnalytics); getFID(sendToAnalytics); getFCP(sendToAnalytics); getLCP(sendToAnalytics); getTTFB(sendToAnalytics); ``` #### Performance Observer ```javascript // Monitor long tasks const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { console.log('Long task detected:', entry); } }); observer.observe({entryTypes: ['longtask']}); // Monitor resource loading const resourceObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.duration > 1000) { console.log('Slow resource:', entry.name, entry.duration); } } }); resourceObserver.observe({entryTypes: ['resource']}); ``` ### Bundle Analysis #### Webpack Bundle Analyzer ```bash # Analyze your bundles npm run build:analyze ``` ```javascript // scripts/analyze.js import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; export default defineConfig({ plugins: [ process.env.ANALYZE && new BundleAnalyzerPlugin() ].filter(Boolean) }); ``` #### Lighthouse CI ```yaml # .github/workflows/lighthouse.yml name: Lighthouse CI on: [push] jobs: lighthouse: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Use Node.js uses: actions/setup-node@v2 with: node-version: '16' - run: npm install - run: npm run build - name: Run Lighthouse CI uses: treosh/lighthouse-ci-action@v8 with: configPath: './lighthouserc.json' ``` --- ## Performance Checklist ### Development Phase - [ ] **Code Splitting**: Implement route-based and component-based splitting - [ ] **Tree Shaking**: Import only needed functions and components - [ ] **Module Loading**: Use dynamic imports for non-critical code - [ ] **Bundle Analysis**: Regularly analyze bundle sizes - [ ] **Dead Code**: Remove unused CSS and JavaScript ### Asset Optimization - [ ] **Images**: Optimize, use WebP format, implement lazy loading - [ ] **Fonts**: Subset fonts, use font-display: swap - [ ] **Icons**: Use icon fonts or SVG sprites - [ ] **Compression**: Enable gzip/brotli compression - [ ] **Minification**: Minify CSS, JavaScript, and HTML ### Caching Strategy - [ ] **Browser Caching**: Set appropriate cache headers - [ ] **CDN**: Use CDN for static assets - [ ] **Service Worker**: Implement for offline functionality - [ ] **Versioning**: Use file hashing for cache busting ### Runtime Performance - [ ] **Event Delegation**: Use for dynamic content - [ ] **Debouncing**: Implement for expensive operations - [ ] **Memory Leaks**: Clean up event listeners and timers - [ ] **DOM Manipulation**: Batch updates and use DocumentFragment ### Monitoring - [ ] **Core Web Vitals**: Monitor LCP, FID, CLS - [ ] **Performance API**: Track loading times - [ ] **Error Tracking**: Monitor JavaScript errors - [ ] **User Experience**: Track real user metrics --- ## Advanced Optimization Techniques ### Preloading Strategies #### Module Preloading ```html ``` #### Predictive Loading ```javascript // Preload modules based on user behavior const observeNavigation = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const href = entry.target.getAttribute('href'); // Preload likely modules for the target page if (href.includes('charts')) { import('./modules/charts.js'); } else if (href.includes('forms')) { import('./modules/forms.js'); } } }); }); // Observe navigation links document.querySelectorAll('a[href]').forEach(link => { observeNavigation.observe(link); }); ``` ### Critical Path Optimization #### Critical CSS Inlining ```html ``` #### Resource Hints ```html ``` --- ## Next Steps - **[Deployment Guide]({{ site.baseurl }}/docs/deployment/)** - Deploy optimized builds - **[Monitoring Guide]({{ site.baseurl }}/docs/monitoring/)** - Set up performance monitoring - **[Troubleshooting]({{ site.baseurl }}/docs/troubleshooting/)** - Solve performance issues --- {: .highlight } 💡 **Pro Tip**: Use the `npm run optimize` command to analyze your current bundle and get personalized optimization recommendations based on your specific usage patterns.