Smart Caching System
BaroCSS features a sophisticated multi-layer caching system that dramatically improves performance by avoiding redundant CSS generation and parsing operations.
The Performance Challenge
CSS generation involves several expensive operations:
- Class name parsing - Tokenizing and analyzing class syntax
- AST generation - Building Abstract Syntax Trees
- CSS compilation - Converting AST to CSS rules
- Style injection - Adding CSS to the DOM
Without caching, these operations would run every time, even for identical classes:
// Without caching - inefficient
element1.className = 'bg-blue-500'; // Parse + AST + CSS + Inject
element2.className = 'bg-blue-500'; // Parse + AST + CSS + Inject (again!)
element3.className = 'bg-blue-500'; // Parse + AST + CSS + Inject (again!)
BaroCSS Smart Caching Solution
BaroCSS implements a multi-layer caching system that stores results at each stage:
// With smart caching - efficient
element1.className = 'bg-blue-500'; // Parse + AST + CSS + Inject + Cache
element2.className = 'bg-blue-500'; // Cache hit! (0.1ms)
element3.className = 'bg-blue-500'; // Cache hit! (0.1ms)
Caching Layers
1. Parse Result Cache
Caches the result of class name parsing:
interface ParseResult {
modifiers: string[];
utility: string;
arbitrary?: string;
}
// Cache key: 'bg-blue-500'
// Cache value: { modifiers: [], utility: 'bg', arbitrary: 'blue-500' }
const parseCache = new Map<string, ParseResult>();
2. AST Cache
Caches generated Abstract Syntax Trees:
interface ASTNode {
type: 'style-rule' | 'at-rule' | 'declaration';
selector?: string;
property?: string;
value?: string;
}
// Cache key: 'bg-blue-500' + context hash
// Cache value: [ASTNode, ASTNode, ...]
const astCache = new Map<string, ASTNode[]>();
3. CSS Cache
Caches final CSS output:
// Cache key: 'bg-blue-500' + context hash
// Cache value: '.bg-blue-500 { background-color: rgb(59 130 246); }'
const cssCache = new Map<string, string>();
4. Utility Cache
Caches utility prefix lookups:
// Cache key: 'bg'
// Cache value: { property: 'background-color', type: 'color' }
const utilityCache = new Map<string, UtilityDefinition>();
5. Failure Cache
Caches invalid class names to avoid reprocessing:
// Cache key: 'invalid-class'
// Cache value: true (failed to parse)
const failureCache = new Set<string>();
How Smart Caching Works
1. Cache-First Processing
function processClass(className: string, ctx: Context) {
// Check CSS cache first
const cssKey = `${className}:${ctx.hash}`;
if (cssCache.has(cssKey)) {
return cssCache.get(cssKey); // Cache hit!
}
// Check AST cache
const astKey = `${className}:${ctx.hash}`;
if (astCache.has(astKey)) {
const ast = astCache.get(astKey);
const css = astToCss(ast);
cssCache.set(cssKey, css);
return css;
}
// Check parse cache
const parseKey = className;
if (parseCache.has(parseKey)) {
const parseResult = parseCache.get(parseKey);
const ast = parseResultToAst(parseResult, ctx);
const css = astToCss(ast);
// Cache results
astCache.set(astKey, ast);
cssCache.set(cssKey, css);
return css;
}
// Full processing pipeline
const parseResult = parseClassName(className);
const ast = parseClassToAst(className, ctx);
const css = astToCss(ast);
// Cache all results
parseCache.set(parseKey, parseResult);
astCache.set(astKey, ast);
cssCache.set(cssKey, css);
return css;
}
2. Context-Aware Caching
Cache keys include context information to handle theme changes:
function getCacheKey(className: string, ctx: Context) {
// Include theme hash in cache key
const themeHash = hashObject(ctx.theme);
return `${className}:${themeHash}`;
}
// Different themes = different cache entries
const lightTheme = createContext({ theme: { colors: { blue: '#3b82f6' } } });
const darkTheme = createContext({ theme: { colors: { blue: '#1e40af' } } });
// These will have different cache entries
processClass('bg-blue-500', lightTheme); // Cache: 'bg-blue-500:light-hash'
processClass('bg-blue-500', darkTheme); // Cache: 'bg-blue-500:dark-hash'
3. Intelligent Cache Invalidation
class SmartCache {
private caches = {
parse: new Map<string, ParseResult>(),
ast: new Map<string, ASTNode[]>(),
css: new Map<string, string>(),
utility: new Map<string, UtilityDefinition>(),
failure: new Set<string>()
};
// Invalidate caches when theme changes
invalidateOnThemeChange(newTheme: Theme) {
const newHash = hashObject(newTheme);
// Clear context-dependent caches
this.caches.ast.clear();
this.caches.css.clear();
// Keep parse and utility caches (theme-independent)
}
// Invalidate specific class patterns
invalidatePattern(pattern: RegExp) {
for (const [key] of this.caches.css) {
if (pattern.test(key)) {
this.caches.css.delete(key);
this.caches.ast.delete(key);
}
}
}
}
Performance Benefits
Memory Efficiency
// Traditional approach: Create new objects every time
function traditionalProcess(className: string) {
const parseResult = parseClassName(className); // New object
const ast = parseClassToAst(className, ctx); // New object
const css = astToCss(ast); // New string
return css;
}
// Smart caching: Reuse cached objects
function cachedProcess(className: string) {
if (cssCache.has(className)) {
return cssCache.get(className); // Reuse cached string
}
// ... process and cache
}
CPU Optimization
// Performance comparison
const classes = ['bg-blue-500', 'text-white', 'p-4'];
// Without caching
const start1 = performance.now();
classes.forEach(cls => traditionalProcess(cls));
const time1 = performance.now() - start1; // ~15ms
// With smart caching
const start2 = performance.now();
classes.forEach(cls => cachedProcess(cls));
const time2 = performance.now() - start2; // ~3ms
console.log(`Caching improved performance by ${time1 / time2}x`);
Cache Hit Rates
Typical cache hit rates in real applications:
const stats = runtime.getStats();
console.log(stats);
// {
// totalClasses: 1000,
// cacheHits: 850,
// cacheMisses: 150,
// hitRate: 0.85, // 85% cache hit rate
// averageTime: '0.8ms', // vs 5ms without caching
// memoryUsage: '2.1MB' // vs 8MB without caching
// }
Advanced Caching Features
1. LRU Cache Management
class LRUCache<K, V> {
private maxSize: number;
private cache = new Map<K, V>();
constructor(maxSize: number = 1000) {
this.maxSize = maxSize;
}
get(key: K): V | undefined {
if (this.cache.has(key)) {
// Move to end (most recently used)
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
return undefined;
}
set(key: K, value: V): void {
if (this.cache.size >= this.maxSize) {
// Remove least recently used
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
}
2. Cache Warming
Pre-populate cache with commonly used classes:
class CacheWarmer {
private commonClasses = [
'bg-white', 'bg-gray-100', 'bg-gray-200',
'text-black', 'text-gray-600', 'text-gray-800',
'p-2', 'p-4', 'p-6', 'p-8',
'm-2', 'm-4', 'm-6', 'm-8'
];
warmCache(ctx: Context) {
this.commonClasses.forEach(className => {
processClass(className, ctx); // Populate cache
});
}
}
3. Cache Statistics
class CacheStats {
private hits = 0;
private misses = 0;
private totalSize = 0;
recordHit() { this.hits++; }
recordMiss() { this.misses++; }
getStats() {
return {
hitRate: this.hits / (this.hits + this.misses),
totalRequests: this.hits + this.misses,
cacheSize: this.totalSize,
memoryEfficiency: this.calculateMemoryEfficiency()
};
}
}
Use Cases
1. High-Traffic Applications
Perfect for applications with many repeated classes:
// E-commerce product grid
const products = await fetchProducts();
products.forEach(product => {
const element = document.createElement('div');
element.className = 'product-card bg-white shadow-md rounded-lg p-4';
// 'product-card bg-white shadow-md rounded-lg p-4' is cached after first use
grid.appendChild(element);
});
2. Component Libraries
Build efficient component systems:
class Button {
private static commonClasses = 'px-4 py-2 rounded font-medium transition-colors';
constructor(variant: 'primary' | 'secondary') {
this.className = `${Button.commonClasses} ${this.getVariantClasses(variant)}`;
// Common classes are cached, only variant classes are processed
}
}
3. Dynamic Theming
Handle theme changes efficiently:
function switchTheme(theme: 'light' | 'dark') {
const newContext = createContext({ theme });
// Clear theme-dependent caches
cache.invalidateOnThemeChange(newContext.theme);
// Process elements with new theme
document.querySelectorAll('[class*="bg-"]').forEach(element => {
const className = element.className;
const css = processClass(className, newContext); // Uses new cache
updateElementStyles(element, css);
});
}
Best Practices
1. Monitor Cache Performance
// Track cache efficiency
setInterval(() => {
const stats = runtime.getStats();
if (stats.hitRate < 0.7) {
console.warn('Low cache hit rate:', stats.hitRate);
}
}, 5000);
2. Optimize Cache Size
// Configure appropriate cache sizes
const runtime = new BrowserRuntime({
cache: {
maxSize: 1000, // Maximum cached classes
maxMemory: '10MB', // Maximum memory usage
ttl: 300000 // Time to live (5 minutes)
}
});
3. Use Semantic Class Names
// Good: Reusable semantic classes
const buttonClasses = 'btn btn-primary btn-large';
// Avoid: Too specific classes
const specificClasses = 'bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600';
4. Warm Cache on Startup
// Pre-populate cache with common classes
const cacheWarmer = new CacheWarmer();
cacheWarmer.warmCache(runtime.context);
Performance Comparison
Scenario | Without Caching | With Smart Caching | Improvement |
---|---|---|---|
First Load | 100ms | 100ms | Same |
Repeated Classes | 50ms | 2ms | 25x faster |
Memory Usage | 8MB | 2MB | 75% less |
Cache Hit Rate | 0% | 85% | Significant |
Average Response | 5ms | 0.8ms | 6x faster |
Conclusion
Smart caching is a cornerstone of BaroCSS's performance, providing dramatic improvements in speed and memory efficiency through intelligent multi-layer caching.
The system automatically manages cache invalidation, provides comprehensive statistics, and scales efficiently for applications of any size.
Ready to experience smart caching? Check out the Performance Guide for optimization tips and best practices.