πŸ“ Volume II: Laravel Performance Tuning Kit

🎨 Topic 26: Blade View Cache

Every Blade template is compiled to PHP. Without caching, this happens on every request.

"Blade templates are beautiful. They compile to PHP.
But compilation takes time β€” 5-20ms per view.
Without view cache, this happens on EVERY request.
php artisan view:cache compiles all views once.
After caching, views load instantly. 10-30ms saved per request."
⚠️ THE VIEW COMPILATION TRAP

Many developers don't realize that Blade templates need to be compiled to PHP before execution. In development, this happens automatically when you change a view. In production, without caching, Laravel recompiles views on every request β€” or at least checks if they need recompilation. With 100+ views, this adds significant overhead.

πŸ”΄ The Problem: View Compilation Overhead

WHAT HAPPENS WHEN YOU RENDER A BLADE VIEW ═══════════════════════════════════════════════════════════════════ WITHOUT VIEW CACHE (Every request): β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 1. Check if view exists: resources/views/home.blade.php β”‚ β”‚ 2. Check compiled view: storage/framework/views/home.php β”‚ β”‚ 3. Compare timestamps (is view newer than compiled?) β”‚ β”‚ 4. If newer OR no compiled file: β”‚ β”‚ β€’ Parse Blade syntax (@if, @foreach, @section, etc.) β”‚ β”‚ β€’ Convert to PHP β”‚ β”‚ β€’ Write PHP file to storage/framework/views/ β”‚ β”‚ β€’ Check if directory is writable β”‚ β”‚ 5. Include compiled PHP file β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ For 100 views on a page: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Each @include('header') β†’ trigger the same process β”‚ β”‚ 100 views = 100 compilation checks β”‚ β”‚ Time: 50-200ms wasted β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ WITH VIEW CACHE (After php artisan view:cache): β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 1. All views pre-compiled during deployment β”‚ β”‚ 2. Directly include the compiled PHP files β”‚ β”‚ 3. NO timestamp checks β”‚ β”‚ 4. NO Blade parsing β”‚ β”‚ β”‚ β”‚ Time: 1-5ms to include all views β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
THE VIEW SINS

πŸ“Š View Compilation Cost: Before vs After

Page Type Views per Page Without Cache With Cache Savings
Simple page (1 view) 1 5-15ms 1-2ms 70-80% reduction
Blog post (layout + content + sidebar) 5-10 15-40ms 2-5ms 70-80% reduction
E-commerce product page (header, footer, product, reviews, related) 15-25 30-80ms 4-10ms 70-80% reduction
Complex dashboard (many widgets) 30-50 60-150ms 10-20ms 70-80% reduction
THE BOTTOM LINE

View caching saves 10-50ms per request on typical Laravel pages. For high-traffic sites with 1M requests/day, that's 10-50 seconds of CPU time saved daily.

βœ… The Solution: php artisan view:cache

THE GOLDEN RULE OF BLADE

Always run php artisan view:cache in production after every deployment.

πŸ‘Ž BAD (No cache)

# Deploy script WITHOUT view cache
git pull
composer install --no-dev
php artisan migrate
php artisan config:cache
php artisan route:cache

# Views are NOT cached
# First request after deploy: compiles ALL views
# Every request: checks timestamps on ALL views

πŸ‘ GOOD (With cache)

# Deploy script WITH view cache
git pull
composer install --no-dev
php artisan migrate
php artisan config:cache
php artisan route:cache
php artisan view:cache   # ← CRITICAL!

# All views pre-compiled during deployment
# No timestamp checks in production
# Views load instantly
THE OPTIMIZE COMMAND (Includes view:cache)
# One command to rule them all
php artisan optimize

# This runs:
# - config:cache
# - route:cache
# - view:cache
# - event:cache

# ALWAYS run optimize after deployment!

πŸ” What view:cache Actually Does

BEFORE view:cache ═══════════════════════════════════════════════════════════════════ resources/views/ β”œβ”€β”€ home.blade.php β”œβ”€β”€ layout.blade.php β”œβ”€β”€ partials/ β”‚ β”œβ”€β”€ header.blade.php β”‚ └── footer.blade.php └── ... storage/framework/views/ └── (empty or few files) On every request: β€’ Blade engine checks each .blade.php modification time β€’ Compiles if newer than cached version β€’ Writes to storage/framework/views/ AFTER view:cache ═══════════════════════════════════════════════════════════════════ resources/views/ β”œβ”€β”€ home.blade.php β”œβ”€β”€ layout.blade.php └── ... storage/framework/views/ β”œβ”€β”€ 1a2b3c4d5e6f7g8h9i0j.php (compiled home.blade.php) β”œβ”€β”€ 2b3c4d5e6f7g8h9i0j1k.php (compiled layout.blade.php) β”œβ”€β”€ 3c4d5e6f7g8h9i0j1k2l.php (compiled header.blade.php) └── ... In production: β€’ No timestamp checks (Laravel assumes cache is fresh) β€’ Directly includes pre-compiled PHP files β€’ 70-80% reduction in view rendering time

⚠️ View Cache Limitations

WHAT VIEW CACHE DOES NOT CACHE
WHEN VIEW CACHE BREAKS

If you add a new view after running view:cache, it won't be compiled until you run it again. Views must exist at cache time.

# Always re-run view:cache after adding new views
php artisan view:cache

# Or clear and re-cache
php artisan view:clear
php artisan view:cache

πŸ”„ Dynamic Views and Cache (The Exception)

FOR DYNAMIC VIEW NAMES
// BAD: Cannot be cached efficiently
view('pages.' . $pageSlug)->render();

// GOOD: Use a mapping or cache the result
$allowedPages = ['home', 'about', 'contact'];
if (in_array($pageSlug, $allowedPages)) {
    view('pages.' . $pageSlug)->render();
}

// BEST: Cache the compiled view output, not the name
$html = Cache::remember("page.{$pageSlug}", 3600, function () use ($pageSlug) {
    return view('pages.' . $pageSlug)->render();
});

🎭 View Composers (Still Run, That's Fine)

VIEW COMPOSERS ARE NOT CACHED

View composers are still executed on every request — this is intentional and good. They prepare the data for the view. Only the Blade→PHP compilation is cached, not the data binding.

// View composer runs on EVERY request (by design)
View::composer('profile', function ($view) {
    $view->with('user', auth()->user());  // Runs per request βœ…
});

// But the view itself is cached
// So: Compilation = once, Data binding = per request
// This is exactly what you want!

πŸ”§ Troubleshooting View Cache

COMMON ISSUES
CLEARING VIEW CACHE
# Clear all compiled views
php artisan view:clear

# Check if cache is enabled
php artisan optimize:status

# Verify views are cached
ls -la storage/framework/views/
# Should see many .php files

πŸ“ Topic 26 Summary: Blade View Cache

Situation Without View Cache With View Cache Improvement
First request after deploy Compiles all views (100-300ms) Already compiled (5-10ms) 90-95% faster
Subsequent requests Checks timestamps (10-50ms) Direct include (1-5ms) 70-90% faster
Page with 20 partials 20 timestamp checks (20-60ms) 0 checks (2-5ms) 80-90% faster
πŸ“Œ THE RULE: Always run php artisan view:cache (or php artisan optimize) after every deployment in production. It's free performance β€” 10-50ms saved per request. Without it, you're wasting CPU checking file timestamps on every request.
NEXT TOPIC PREVIEW

Topic 27: CAP Theorem β€” Consistency vs Availability vs Partition Tolerance. Why you can't have all three in distributed systems. How to choose the right trade-off for your app.