diff --git a/PERFORMANCE_OPTIMIZATIONS.md b/PERFORMANCE_OPTIMIZATIONS.md deleted file mode 100644 index 68c2582c..00000000 --- a/PERFORMANCE_OPTIMIZATIONS.md +++ /dev/null @@ -1,159 +0,0 @@ -# Performance Optimizations Summary - -## **πŸš€ Major Improvements Implemented** - -### **Before Optimization:** -- **Single Bundle**: 779 KB JS (241 KB gzipped) - Everything loaded on every page -- **No Code Splitting**: Monolithic loading causing slow initial page loads -- **Unused Dependencies**: Chart.js, DataTables, etc. loaded even on simple pages -- **Large Images**: Unoptimized 290KB+ images -- **Poor Caching**: No intelligent asset naming for browser caching - -### **After Optimization:** -- **Split Bundles**: Code split into logical chunks (5 vendor chunks + main) -- **Conditional Loading**: Page-specific modules load only when needed -- **Asset Optimization**: Intelligent file naming and organization -- **Build Optimization**: Console logs removed, terser minification enabled - ---- - -## **πŸ“Š Bundle Size Improvements** - -### **JavaScript Bundles:** -- **main.js**: 79 KB (was 779 KB) - **90% reduction!** -- **vendor-core**: 164 KB (Bootstrap, jQuery, essentials) -- **vendor-charts**: 219 KB (Chart.js, JQVMap - only loads on chart pages) -- **vendor-forms**: 200 KB (Select2, DatePickers - only loads on form pages) -- **vendor-ui**: 13 KB (jQuery UI components) -- **vendor-utils**: 74 KB (Utilities like dayjs, sparkline) - -### **Total Initial Load** (typical page): -- **Before**: 779 KB JS + 527 KB CSS = **1.3 MB** -- **After**: 79 KB + 164 KB + 527 KB = **770 KB** - **40% reduction!** - ---- - -## **🎯 Optimization Strategy Details** - -### **1. Code Splitting & Modular Loading** -```javascript -// Pages now load only what they need -// Dashboard pages: main + vendor-core + vendor-charts + vendor-ui -// Form pages: main + vendor-core + vendor-forms -// Simple pages: main + vendor-core only - -// Dynamic loading system -await loadModule('charts'); // Only loads when charts are needed -await loadModule('forms'); // Only loads when forms are needed -``` - -### **2. Intelligent Asset Management** -```javascript -// Organized asset structure for better caching -dist/ -β”œβ”€β”€ js/[name]-[hash].js // JavaScript with cache-busting -β”œβ”€β”€ images/[name]-[hash].* // Images with cache-busting -β”œβ”€β”€ fonts/[name]-[hash].* // Fonts with cache-busting -└── assets/[name]-[hash].* // Other assets -``` - -### **3. Build Optimizations** -- βœ… **Terser minification** with console.log removal -- βœ… **Manual chunk splitting** for optimal loading -- βœ… **Asset optimization** with intelligent naming -- βœ… **Dead code elimination** in production builds - ---- - -## **πŸ“ˆ Performance Impact** - -### **Page Load Times** (estimated improvements): -- **Dashboard Pages**: 40-50% faster initial load -- **Form Pages**: 50-60% faster initial load -- **Simple Pages**: 60-70% faster initial load -- **Chart Pages**: 30-40% faster (charts load on-demand) - -### **User Experience Improvements:** -- βœ… **Faster Time to Interactive** - Critical code loads first -- βœ… **Better Caching** - Vendor code cached separately from app code -- βœ… **Progressive Loading** - Pages usable before all features load -- βœ… **Reduced Memory Usage** - Only necessary code in memory - ---- - -## **πŸ”§ Implementation Details** - -### **Core Files Created:** -- `src/main-core.js` - Essential libraries only (79 KB) -- `src/modules/charts.js` - Chart-specific code (219 KB) -- `src/modules/forms.js` - Form-specific code (200 KB) -- `src/modules/tables.js` - Table-specific code -- `src/modules/dashboard.js` - Dashboard widgets - -### **Dynamic Loading System:** -```javascript -// Automatic loading based on page needs -if (document.querySelector('.chart-container')) { - await loadModule('charts'); -} -if (document.querySelector('form.advanced')) { - await loadModule('forms'); -} -``` - ---- - -## **🎁 Additional Benefits** - -### **Development Benefits:** -- βœ… **Faster Development Builds** - Better hot module replacement -- βœ… **Easier Debugging** - Smaller, focused bundles -- βœ… **Better Error Tracking** - Clear module boundaries -- βœ… **Simplified Testing** - Isolated functionality - -### **SEO & Performance Benefits:** -- βœ… **Better Core Web Vitals** - Improved LCP, FID, CLS scores -- βœ… **Mobile Performance** - Crucial for mobile users -- βœ… **Search Engine Ranking** - Page speed is a ranking factor -- βœ… **User Retention** - Faster pages = better user experience - ---- - -## **πŸ“‹ Next Steps & Recommendations** - -### **Immediate Opportunities:** -1. **Image Optimization**: Convert 290KB images to WebP format (60-80% size reduction) -2. **Lazy Loading**: Implement lazy loading for images and heavy components -3. **Service Worker**: Add caching strategy for offline support - -### **Advanced Optimizations:** -1. **Route-based Code Splitting**: Split based on navigation routes -2. **Component-level Splitting**: Split individual UI components -3. **Preloading Strategy**: Preload likely-needed modules - -### **Monitoring:** -- Use `npm run build:analyze` to track bundle sizes over time -- Monitor Core Web Vitals in production -- Set up performance budgets for CI/CD - ---- - -## **πŸš€ Commands** - -```bash -# Build with optimization analysis -npm run build:analyze - -# Run optimization analysis only -npm run optimize - -# Development with optimizations -npm run dev - -# Production build -npm run build -``` - ---- - -**Result**: From 1.3MB initial load to 770KB (40% reduction) with much better caching and progressive loading! \ No newline at end of file diff --git a/README.md b/README.md index 55ee02f8..39663cd9 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,7 @@ Gentelella has been integrated into various frameworks: - **[Django](https://github.com/GiriB/django-gentelella)** - Python Django app - **[Angular](https://github.com/kmkatsma/angular2-webpack-starter-gentelella)** - Angular integration - **[React](https://github.com/thomaslwq/react-admin)** - React implementation -- **[Symfony](https://github.com/mamless/Gentella-admin-Symfony-5)** - Symfony 5 integration +- **[Symfony](https://github.com/mamless/Gentella-admin-Symfony-6)** - Symfony 6 integration - **[Yii](https://github.com/yiister/yii2-gentelella)** - Yii framework integration - **[Flask](https://github.com/afourmy/flask-gentelella)** - Python Flask app - **[CakePHP](https://github.com/backstageel/cakephp-gentelella-theme)** - CakePHP integration diff --git a/docs/performance.md b/docs/performance.md deleted file mode 100644 index 8873a590..00000000 --- a/docs/performance.md +++ /dev/null @@ -1,686 +0,0 @@ ---- -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. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5d0819e8..dee80c12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,6 @@ "echarts": "^5.6.0", "fastclick": "^1.0.6", "flot": "^4.2.6", - "gauge.js": "^0.2.1", "gaugeJS": "^1.3.9", "icheck": "^1.0.2", "ion-rangeslider": "^2.3.1", @@ -1582,12 +1581,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/gauge.js": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/gauge.js/-/gauge.js-0.2.1.tgz", - "integrity": "sha512-kkpKXyL5uKg1wdU9q6XQ67s2klhH4Cuw0NzU5rCvMHqRy70MB4w/6MUk3Y22+IjJJ9hERFkqLhi3iafjtmcvFg==", - "license": "MIT" - }, "node_modules/gaugeJS": { "version": "1.3.9", "resolved": "https://registry.npmjs.org/gaugeJS/-/gaugeJS-1.3.9.tgz", diff --git a/package.json b/package.json index 2cab48f7..073cdc18 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,6 @@ "echarts": "^5.6.0", "fastclick": "^1.0.6", "flot": "^4.2.6", - "gauge.js": "^0.2.1", "gaugeJS": "^1.3.9", "icheck": "^1.0.2", "ion-rangeslider": "^2.3.1", diff --git a/production/calendar.html b/production/calendar.html index 0475cc86..a0861577 100755 --- a/production/calendar.html +++ b/production/calendar.html @@ -49,9 +49,10 @@