Skip to main content

Course Progress

Loading...

⚡ Asset Optimization and Best Practices

Maximize WordPress theme performance

Learn minification, compression, lazy loading, and advanced optimization techniques

Learning Objectives

  • Understand performance metrics and goals
  • Implement asset minification and compression
  • Master lazy loading techniques
  • Optimize critical rendering path
  • Implement browser caching strategies
  • Use CDNs effectively
  • Optimize images and media
  • Apply performance best practices

Why Asset Optimization Matters

Website performance directly impacts user experience, SEO rankings, and conversion rates. Optimizing assets in WordPress themes is crucial for creating fast, efficient websites.

First Contentful Paint

< 1.8s
Good performance target

Largest Contentful Paint

< 2.5s
Main content visible

Time to Interactive

< 3.8s
Page fully interactive

Cumulative Layout Shift

< 0.1
Visual stability
💡
Key Insight
A 1-second delay in page load time can result in a 7% reduction in conversions and 11% fewer page views.

Minification and Compression

Asset Minification

WordPress Minification Setup

<?php
// functions.php

class ThemeAssetOptimizer {
    private $minify_css = true;
    private $minify_js = true;
    private $combine_files = true;
    
    public function __construct() {
        add_action('wp_enqueue_scripts', array($this, 'optimize_assets'), 999);
        add_filter('script_loader_tag', array($this, 'add_async_defer'), 10, 3);
        add_filter('style_loader_tag', array($this, 'optimize_css_loading'), 10, 4);
    }
    
    public function optimize_assets() {
        if (!is_admin() && !is_customize_preview()) {
            // Remove version strings
            add_filter('script_loader_src', array($this, 'remove_version_strings'), 15, 1);
            add_filter('style_loader_src', array($this, 'remove_version_strings'), 15, 1);
            
            // Conditionally load assets
            $this->conditional_loading();
            
            // Load minified versions in production
            if (!WP_DEBUG) {
                $this->load_minified_assets();
            }
        }
    }
    
    private function load_minified_assets() {
        // Dequeue original and enqueue minified
        if (wp_style_is('theme-style', 'enqueued')) {
            wp_dequeue_style('theme-style');
            wp_enqueue_style(
                'theme-style-min',
                get_template_directory_uri() . '/assets/css/style.min.css',
                array(),
                filemtime(get_template_directory() . '/assets/css/style.min.css')
            );
        }
        
        if (wp_script_is('theme-script', 'enqueued')) {
            wp_dequeue_script('theme-script');
            wp_enqueue_script(
                'theme-script-min',
                get_template_directory_uri() . '/assets/js/main.min.js',
                array(),
                filemtime(get_template_directory() . '/assets/js/main.min.js'),
                true
            );
        }
    }
    
    private function conditional_loading() {
        // Only load contact form scripts on contact page
        if (!is_page('contact')) {
            wp_dequeue_script('contact-form-7');
            wp_dequeue_style('contact-form-7');
        }
        
        // Only load comment scripts on single posts with comments
        if (!is_singular() || !comments_open()) {
            wp_dequeue_script('comment-reply');
        }
        
        // Remove block library CSS if not using Gutenberg
        if (!has_blocks()) {
            wp_dequeue_style('wp-block-library');
            wp_dequeue_style('wp-block-library-theme');
        }
    }
    
    public function add_async_defer($tag, $handle, $src) {
        // Async loading for non-critical scripts
        $async_scripts = array('google-analytics', 'facebook-pixel');
        if (in_array($handle, $async_scripts)) {
            return str_replace(' src', ' async src', $tag);
        }
        
        // Defer loading for other scripts
        $defer_scripts = array('theme-script-min', 'animations');
        if (in_array($handle, $defer_scripts)) {
            return str_replace(' src', ' defer src', $tag);
        }
        
        return $tag;
    }
    
    public function optimize_css_loading($html, $handle, $href, $media) {
        // Preload critical CSS
        $critical_styles = array('theme-critical', 'above-fold');
        if (in_array($handle, $critical_styles)) {
            $html = sprintf(
                '<link rel="preload" as="style" href="%s" onload="this.onload=null;this.rel=\'stylesheet\'">
                <noscript>%s</noscript>',
                $href,
                $html
            );
        }
        
        return $html;
    }
    
    public function remove_version_strings($src) {
        if (strpos($src, 'ver=')) {
            $src = remove_query_arg('ver', $src);
        }
        return $src;
    }
}

// Initialize optimizer
new ThemeAssetOptimizer();

Gulp Task for Minification

// gulpfile.js
const gulp = require('gulp');
const uglify = require('gulp-uglify');
const cleanCSS = require('gulp-clean-css');
const concat = require('gulp-concat');
const sourcemaps = require('gulp-sourcemaps');
const imagemin = require('gulp-imagemin');
const gzip = require('gulp-gzip');

// Minify JavaScript
gulp.task('minify-js', () => {
    return gulp.src('assets/js/*.js')
        .pipe(sourcemaps.init())
        .pipe(concat('main.min.js'))
        .pipe(uglify({
            compress: {
                drop_console: true,
                drop_debugger: true
            }
        }))
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest('dist/js'))
        .pipe(gzip())
        .pipe(gulp.dest('dist/js'));
});

// Minify CSS
gulp.task('minify-css', () => {
    return gulp.src('assets/css/*.css')
        .pipe(sourcemaps.init())
        .pipe(concat('style.min.css'))
        .pipe(cleanCSS({
            level: {
                1: {
                    specialComments: 0
                },
                2: {
                    mergeMedia: true,
                    removeEmpty: true,
                    removeDuplicates: true
                }
            }
        }))
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest('dist/css'))
        .pipe(gzip())
        .pipe(gulp.dest('dist/css'));
});

// Optimize images
gulp.task('optimize-images', () => {
    return gulp.src('assets/images/**/*')
        .pipe(imagemin([
            imagemin.gifsicle({interlaced: true}),
            imagemin.mozjpeg({quality: 75, progressive: true}),
            imagemin.optipng({optimizationLevel: 5}),
            imagemin.svgo({
                plugins: [
                    {removeViewBox: true},
                    {cleanupIDs: false}
                ]
            })
        ]))
        .pipe(gulp.dest('dist/images'));
});

// Watch task
gulp.task('watch', () => {
    gulp.watch('assets/js/*.js', gulp.series('minify-js'));
    gulp.watch('assets/css/*.css', gulp.series('minify-css'));
    gulp.watch('assets/images/**/*', gulp.series('optimize-images'));
});

// Default task
gulp.task('default', gulp.parallel('minify-js', 'minify-css', 'optimize-images'));

Implementing Lazy Loading

Native Lazy Loading

<?php
// functions.php

// Add lazy loading to images
function mytheme_add_lazy_loading($content) {
    // Add loading="lazy" to images
    $content = preg_replace(
        '/<img(.*?)src=/i',
        '<img$1loading="lazy" src=',
        $content
    );
    
    // Add loading="lazy" to iframes
    $content = preg_replace(
        '/<iframe(.*?)src=/i',
        '<iframe$1loading="lazy" src=',
        $content
    );
    
    return $content;
}
add_filter('the_content', 'mytheme_add_lazy_loading');

// Lazy load post thumbnails
function mytheme_lazy_load_thumbnails($attr, $attachment, $size) {
    $attr['loading'] = 'lazy';
    
    // Add data attributes for advanced lazy loading
    $attr['data-src'] = $attr['src'];
    $attr['src'] = 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 1 1\'%3E%3C/svg%3E';
    $attr['class'] = isset($attr['class']) ? $attr['class'] . ' lazyload' : 'lazyload';
    
    return $attr;
}
add_filter('wp_get_attachment_image_attributes', 'mytheme_lazy_load_thumbnails', 10, 3);

JavaScript Lazy Loading with Intersection Observer

// lazy-load.js
class LazyLoader {
    constructor() {
        this.images = document.querySelectorAll('img[data-src]');
        this.videos = document.querySelectorAll('video[data-src]');
        this.iframes = document.querySelectorAll('iframe[data-src]');
        
        this.imageOptions = {
            threshold: 0.01,
            rootMargin: '50px'
        };
        
        this.init();
    }
    
    init() {
        if ('IntersectionObserver' in window) {
            this.createObserver();
        } else {
            // Fallback for older browsers
            this.loadAllImages();
        }
    }
    
    createObserver() {
        const imageObserver = new IntersectionObserver((entries, observer) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    this.loadElement(entry.target);
                    observer.unobserve(entry.target);
                }
            });
        }, this.imageOptions);
        
        // Observe all lazy elements
        this.images.forEach(img => imageObserver.observe(img));
        this.videos.forEach(video => imageObserver.observe(video));
        this.iframes.forEach(iframe => imageObserver.observe(iframe));
    }
    
    loadElement(element) {
        const src = element.dataset.src;
        const srcset = element.dataset.srcset;
        
        if (!src) return;
        
        // Handle different element types
        if (element.tagName === 'IMG') {
            this.loadImage(element, src, srcset);
        } else if (element.tagName === 'VIDEO') {
            this.loadVideo(element, src);
        } else if (element.tagName === 'IFRAME') {
            this.loadIframe(element, src);
        }
    }
    
    loadImage(img, src, srcset) {
        // Preload image
        const tempImg = new Image();
        
        tempImg.onload = () => {
            img.src = src;
            if (srcset) {
                img.srcset = srcset;
            }
            img.classList.add('loaded');
            img.classList.remove('lazyload');
        };
        
        tempImg.src = src;
        if (srcset) {
            tempImg.srcset = srcset;
        }
    }
    
    loadVideo(video, src) {
        video.src = src;
        video.load();
        video.classList.add('loaded');
    }
    
    loadIframe(iframe, src) {
        iframe.src = src;
        iframe.classList.add('loaded');
    }
    
    loadAllImages() {
        // Fallback: load all images immediately
        this.images.forEach(img => {
            if (img.dataset.src) {
                img.src = img.dataset.src;
                if (img.dataset.srcset) {
                    img.srcset = img.dataset.srcset;
                }
            }
        });
    }
}

// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', () => {
    new LazyLoader();
});

// Reinitialize after AJAX content loads
document.addEventListener('ajax-content-loaded', () => {
    new LazyLoader();
});

Background Image Lazy Loading

// Lazy load background images
class BackgroundLazyLoader {
    constructor() {
        this.elements = document.querySelectorAll('[data-bg]');
        this.init();
    }
    
    init() {
        if ('IntersectionObserver' in window) {
            const bgObserver = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        const element = entry.target;
                        const bg = element.dataset.bg;
                        
                        element.style.backgroundImage = `url(${bg})`;
                        element.classList.add('bg-loaded');
                        
                        bgObserver.unobserve(element);
                    }
                });
            });
            
            this.elements.forEach(el => bgObserver.observe(el));
        } else {
            // Immediate load for unsupported browsers
            this.elements.forEach(el => {
                el.style.backgroundImage = `url(${el.dataset.bg})`;
            });
        }
    }
}

new BackgroundLazyLoader();

Critical CSS and Above-the-Fold Optimization

Inline Critical CSS

<?php
// functions.php

function mytheme_inline_critical_css() {
    $critical_css_file = get_template_directory() . '/assets/css/critical.min.css';
    
    if (file_exists($critical_css_file)) {
        $critical_css = file_get_contents($critical_css_file);
        echo '<style id="critical-css">' . $critical_css . '</style>';
    }
}
add_action('wp_head', 'mytheme_inline_critical_css', 5);

// Load main CSS asynchronously
function mytheme_async_styles() {
    ?>
    <script>
    // Async CSS loading
    function loadCSS(href) {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = href;
        link.media = 'print';
        link.onload = function() { this.media = 'all'; };
        document.head.appendChild(link);
    }
    
    // Load non-critical CSS
    loadCSS('<?php echo get_template_directory_uri(); ?>/assets/css/style.min.css');
    </script>
    
    <noscript>
        <link rel="stylesheet" href="<?php echo get_template_directory_uri(); ?>/assets/css/style.min.css">
    </noscript>
    <?php
}
add_action('wp_head', 'mytheme_async_styles', 100);

Generate Critical CSS with Node

// generate-critical.js
const critical = require('critical');

critical.generate({
    base: './',
    src: 'index.html',
    css: ['assets/css/style.css'],
    width: 1300,
    height: 900,
    target: {
        css: 'assets/css/critical.min.css',
        uncritical: 'assets/css/uncritical.min.css'
    },
    minify: true,
    extract: true,
    ignore: {
        atrule: ['@font-face'],
        decl: (node, value) => /url\(/.test(value)
    }
});

Resource Hints

<?php
// Add resource hints for faster loading
function mytheme_resource_hints($hints, $relation_type) {
    // DNS Prefetch for external domains
    if ('dns-prefetch' === $relation_type) {
        $hints[] = '//fonts.googleapis.com';
        $hints[] = '//fonts.gstatic.com';
        $hints[] = '//cdnjs.cloudflare.com';
        $hints[] = '//www.google-analytics.com';
    }
    
    // Preconnect for critical resources
    if ('preconnect' === $relation_type) {
        $hints[] = 'https://fonts.googleapis.com';
        $hints[] = 'https://fonts.gstatic.com';
    }
    
    return $hints;
}
add_filter('wp_resource_hints', 'mytheme_resource_hints', 10, 2);

// Preload critical resources
function mytheme_preload_resources() {
    // Preload fonts
    echo '<link rel="preload" as="font" type="font/woff2" crossorigin href="' . 
         get_template_directory_uri() . '/assets/fonts/main-font.woff2">';
    
    // Preload hero image
    if (is_front_page()) {
        $hero_image = get_theme_mod('hero_image');
        if ($hero_image) {
            echo '<link rel="preload" as="image" href="' . esc_url($hero_image) . '">';
        }
    }
    
    // Prefetch next page
    if (is_single()) {
        $next_post = get_next_post();
        if ($next_post) {
            echo '<link rel="prefetch" href="' . get_permalink($next_post) . '">';
        }
    }
}
add_action('wp_head', 'mytheme_preload_resources', 2);

Browser Caching and Service Workers

.htaccess Caching Rules

# Browser Caching
<IfModule mod_expires.c>
    ExpiresActive On
    
    # Images
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/jpg "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    
    # CSS and JavaScript
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
    ExpiresByType text/javascript "access plus 1 month"
    
    # Fonts
    ExpiresByType font/woff2 "access plus 1 year"
    ExpiresByType font/woff "access plus 1 year"
    ExpiresByType font/ttf "access plus 1 year"
    ExpiresByType font/otf "access plus 1 year"
    
    # HTML
    ExpiresByType text/html "access plus 0 seconds"
    
    # Default
    ExpiresDefault "access plus 2 days"
</IfModule>

# Cache-Control Headers
<IfModule mod_headers.c>
    # Images
    <FilesMatch "\.(jpg|jpeg|png|gif|svg|webp|ico)$">
        Header set Cache-Control "public, max-age=31536000, immutable"
    </FilesMatch>
    
    # CSS and JS
    <FilesMatch "\.(css|js)$">
        Header set Cache-Control "public, max-age=2592000"
    </FilesMatch>
    
    # Fonts
    <FilesMatch "\.(woff|woff2|ttf|otf|eot)$">
        Header set Cache-Control "public, max-age=31536000"
    </FilesMatch>
    
    # Disable caching for dynamic content
    <FilesMatch "\.(php|html?)$">
        Header set Cache-Control "no-cache, no-store, must-revalidate"
        Header set Pragma "no-cache"
        Header set Expires "0"
    </FilesMatch>
</IfModule>

# Enable Gzip Compression
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html
    AddOutputFilterByType DEFLATE text/css
    AddOutputFilterByType DEFLATE text/javascript
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE application/json
    AddOutputFilterByType DEFLATE application/xml
    AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>

Service Worker for Offline Caching

// service-worker.js
const CACHE_NAME = 'mytheme-v1.0.0';
const urlsToCache = [
    '/',
    '/wp-content/themes/mytheme/assets/css/critical.min.css',
    '/wp-content/themes/mytheme/assets/js/main.min.js',
    '/wp-content/themes/mytheme/assets/fonts/main-font.woff2',
    '/offline.html'
];

// Install event
self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(cache => {
                console.log('Opened cache');
                return cache.addAll(urlsToCache);
            })
    );
});

// Fetch event
self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request)
            .then(response => {
                // Cache hit - return response
                if (response) {
                    return response;
                }
                
                // Clone the request
                const fetchRequest = event.request.clone();
                
                return fetch(fetchRequest).then(response => {
                    // Check if valid response
                    if (!response || response.status !== 200 || response.type !== 'basic') {
                        return response;
                    }
                    
                    // Clone the response
                    const responseToCache = response.clone();
                    
                    // Cache the response
                    caches.open(CACHE_NAME)
                        .then(cache => {
                            cache.put(event.request, responseToCache);
                        });
                    
                    return response;
                });
            })
            .catch(() => {
                // Return offline page for navigation requests
                if (event.request.mode === 'navigate') {
                    return caches.match('/offline.html');
                }
            })
    );
});

// Activate event
self.addEventListener('activate', event => {
    const cacheWhitelist = [CACHE_NAME];
    
    event.waitUntil(
        caches.keys().then(cacheNames => {
            return Promise.all(
                cacheNames.map(cacheName => {
                    if (cacheWhitelist.indexOf(cacheName) === -1) {
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
});

Register Service Worker

<?php
// Register service worker
function mytheme_register_service_worker() {
    ?>
    <script>
    if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
            navigator.serviceWorker.register('<?php echo get_template_directory_uri(); ?>/service-worker.js')
                .then(function(registration) {
                    console.log('ServiceWorker registration successful');
                }, function(err) {
                    console.log('ServiceWorker registration failed: ', err);
                });
        });
    }
    </script>
    <?php
}
add_action('wp_footer', 'mytheme_register_service_worker');

Image and Media Optimization

Responsive Images with srcset

<?php
// Generate responsive images
function mytheme_responsive_image($attachment_id, $sizes = array()) {
    $default_sizes = array(
        '(max-width: 480px) 480px',
        '(max-width: 768px) 768px',
        '(max-width: 1024px) 1024px',
        '1920px'
    );
    
    $sizes = !empty($sizes) ? $sizes : $default_sizes;
    $img_src = wp_get_attachment_image_src($attachment_id, 'full');
    $img_srcset = wp_get_attachment_image_srcset($attachment_id, 'full');
    $img_sizes = implode(', ', $sizes);
    
    $output = sprintf(
        '<img src="%s" srcset="%s" sizes="%s" loading="lazy" alt="%s">',
        esc_url($img_src[0]),
        esc_attr($img_srcset),
        esc_attr($img_sizes),
        esc_attr(get_post_meta($attachment_id, '_wp_attachment_image_alt', true))
    );
    
    return $output;
}

// Add WebP support
function mytheme_webp_support($mimes) {
    $mimes['webp'] = 'image/webp';
    return $mimes;
}
add_filter('mime_types', 'mytheme_webp_support');

// Serve WebP images when available
function mytheme_serve_webp($image_url) {
    $image_path = str_replace(site_url(), ABSPATH, $image_url);
    $webp_path = preg_replace('/\.(jpg|jpeg|png)$/i', '.webp', $image_path);
    $webp_url = preg_replace('/\.(jpg|jpeg|png)$/i', '.webp', $image_url);
    
    if (file_exists($webp_path) && mytheme_browser_supports_webp()) {
        return $webp_url;
    }
    
    return $image_url;
}

function mytheme_browser_supports_webp() {
    return isset($_SERVER['HTTP_ACCEPT']) && 
           strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false;
}

Optimize Upload Images

<?php
// Automatically optimize uploaded images
function mytheme_optimize_uploaded_image($metadata, $attachment_id) {
    $uploads_dir = wp_upload_dir();
    $file_path = $uploads_dir['basedir'] . '/' . $metadata['file'];
    
    // Get image type
    $image_type = wp_check_filetype($file_path);
    
    switch ($image_type['type']) {
        case 'image/jpeg':
        case 'image/jpg':
            $image = imagecreatefromjpeg($file_path);
            imagejpeg($image, $file_path, 85); // 85% quality
            break;
            
        case 'image/png':
            $image = imagecreatefrompng($file_path);
            imagepng($image, $file_path, 8); // Compression level 8
            break;
    }
    
    if (isset($image)) {
        imagedestroy($image);
    }
    
    // Optimize thumbnails
    if (isset($metadata['sizes'])) {
        foreach ($metadata['sizes'] as $size => $data) {
            $thumb_path = $uploads_dir['basedir'] . '/' . 
                         dirname($metadata['file']) . '/' . $data['file'];
            
            // Apply same optimization to thumbnails
            if (file_exists($thumb_path)) {
                // Optimization code here
            }
        }
    }
    
    return $metadata;
}
add_filter('wp_generate_attachment_metadata', 'mytheme_optimize_uploaded_image', 10, 2);

Performance Monitoring

Performance Monitoring Script

// performance-monitor.js
class PerformanceMonitor {
    constructor() {
        this.metrics = {};
        this.init();
    }
    
    init() {
        if ('performance' in window && 'PerformanceObserver' in window) {
            this.observePerformance();
            this.measureMetrics();
        }
    }
    
    observePerformance() {
        // Observe Largest Contentful Paint
        const lcpObserver = new PerformanceObserver((list) => {
            const entries = list.getEntries();
            const lastEntry = entries[entries.length - 1];
            this.metrics.lcp = lastEntry.renderTime || lastEntry.loadTime;
            console.log('LCP:', this.metrics.lcp);
        });
        lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
        
        // Observe First Input Delay
        const fidObserver = new PerformanceObserver((list) => {
            const entries = list.getEntries();
            entries.forEach(entry => {
                this.metrics.fid = entry.processingStart - entry.startTime;
                console.log('FID:', this.metrics.fid);
            });
        });
        fidObserver.observe({ entryTypes: ['first-input'] });
        
        // Observe Cumulative Layout Shift
        let clsValue = 0;
        const clsObserver = new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
                if (!entry.hadRecentInput) {
                    clsValue += entry.value;
                }
            }
            this.metrics.cls = clsValue;
            console.log('CLS:', this.metrics.cls);
        });
        clsObserver.observe({ entryTypes: ['layout-shift'] });
    }
    
    measureMetrics() {
        window.addEventListener('load', () => {
            const perfData = performance.getEntriesByType('navigation')[0];
            
            // Calculate metrics
            this.metrics.domContentLoaded = perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart;
            this.metrics.loadComplete = perfData.loadEventEnd - perfData.loadEventStart;
            this.metrics.domInteractive = perfData.domInteractive;
            this.metrics.firstByte = perfData.responseStart - perfData.requestStart;
            
            // Send to analytics
            this.sendMetrics();
        });
    }
    
    sendMetrics() {
        // Send to Google Analytics
        if (typeof gtag !== 'undefined') {
            gtag('event', 'performance', {
                'event_category': 'Web Vitals',
                'event_label': 'Page Load',
                'value': Math.round(this.metrics.lcp),
                'metric_lcp': this.metrics.lcp,
                'metric_fid': this.metrics.fid,
                'metric_cls': this.metrics.cls
            });
        }
        
        // Or send to custom endpoint
        fetch('/wp-json/mytheme/v1/performance', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(this.metrics)
        });
    }
}

// Initialize
new PerformanceMonitor();

Complete Optimization Checklist

Pre-Launch Optimization Checklist

  • Minify all CSS and JavaScript files
  • Combine and bundle assets where appropriate
  • Implement lazy loading for images and videos
  • Generate and inline critical CSS
  • Add resource hints (dns-prefetch, preconnect, preload)
  • Optimize and compress all images
  • Provide WebP alternatives for images
  • Configure browser caching headers
  • Enable Gzip/Brotli compression
  • Remove unused CSS and JavaScript
  • Defer non-critical JavaScript
  • Optimize web fonts loading
  • Implement service worker for offline support
  • Remove query strings from static resources
  • Optimize database queries
  • Use CDN for static assets
  • Test with PageSpeed Insights
  • Monitor Core Web Vitals
  • Implement performance budgets
  • Document optimization strategies

Best Practices

Asset Optimization Best Practices

  • Mobile-first approach: Optimize for mobile devices first
  • Progressive enhancement: Start with basic functionality
  • Performance budgets: Set limits for page weight and load time
  • Regular audits: Use Lighthouse and WebPageTest regularly
  • Monitor real users: Track Core Web Vitals in production
  • Optimize critical path: Prioritize above-the-fold content
  • Use modern formats: WebP, AVIF for images, WOFF2 for fonts
  • Eliminate render-blocking: Async/defer scripts, critical CSS
  • Cache aggressively: Use long cache lifetimes with versioning
  • Test thoroughly: Check performance across devices and networks
Over-optimization can lead to maintainability issues. Find the right balance between performance and code complexity. Always measure the impact of optimizations.
Use Chrome DevTools' Coverage tab to identify unused CSS and JavaScript. This can help you eliminate dead code and reduce bundle sizes.

Practice Exercise

💻
Optimize Your Theme

Apply optimization techniques to your theme:

  1. Set up build process for minification
  2. Implement lazy loading for all images
  3. Extract and inline critical CSS
  4. Configure browser caching in .htaccess
  5. Optimize all images with compression
  6. Add resource hints for external resources
  7. Implement service worker for offline
  8. Defer non-critical JavaScript
  9. Remove unused CSS with PurgeCSS
  10. Achieve 90+ PageSpeed score

Additional Resources