Caching
binja includes an LRU (Least Recently Used) cache for compiled templates, improving performance by avoiding repeated parsing.
Enable Caching
const env = new Environment({ templates: './views', cache: true, // Enable caching (default: true)})Cache Configuration
const env = new Environment({ templates: './views', cache: true, cacheMaxSize: 100, // Maximum cached templates (default: 100)})How It Works
- First render: Template is lexed, parsed, and executed
- Subsequent renders: Cached AST is reused, skipping lex/parse
First render: Template → Lexer → Parser → AST → Runtime → OutputCached render: Template → [Cache Hit] → AST → Runtime → OutputCache Monitoring
Cache Size
const size = env.cacheSize()console.log(`${size} templates cached`)Cache Statistics
const stats = env.cacheStats()console.log(stats)// {// size: 10, // Current cached templates// maxSize: 100, // Maximum cache size// hits: 150, // Cache hits// misses: 10, // Cache misses// hitRate: 0.94 // Hit rate (93.75%)// }Monitor Performance
// Log cache stats periodicallysetInterval(() => { const stats = env.cacheStats() console.log(`Cache: ${stats.size}/${stats.maxSize}, Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`)
if (stats.hitRate < 0.8) { console.warn('Low cache hit rate - consider increasing cacheMaxSize') }}, 60000)Cache Management
Clear Cache
env.clearCache()// Clears all cached templates and resets statsPre-warm Cache
Load frequently-used templates at startup:
const env = new Environment({ templates: './views', cache: true,})
// Pre-warm cache at startupawait env.loadTemplate('base.html')await env.loadTemplate('layouts/default.html')await env.loadTemplate('components/header.html')await env.loadTemplate('components/footer.html')Cache Key
Templates are cached by their path:
// These use the same cache entryawait env.render('home.html', { a: 1 })await env.render('home.html', { a: 2 })
// Different cache entriesawait env.render('home.html', ctx)await env.render('about.html', ctx)LRU Eviction
When cache reaches cacheMaxSize, least recently used templates are evicted:
const env = new Environment({ cacheMaxSize: 50, // Only 50 templates cached})
// After 51 unique templates, oldest is evictedDevelopment vs Production
Development
Consider disabling cache for hot reloading:
const env = new Environment({ templates: './views', cache: process.env.NODE_ENV === 'production',})Or use smaller cache:
const env = new Environment({ templates: './views', cache: true, cacheMaxSize: 10, // Smaller cache for dev})Production
Enable caching with appropriate size:
const env = new Environment({ templates: './views', cache: true, cacheMaxSize: 200, // Adjust based on template count})Memory Considerations
Each cached template consumes memory for its AST. Monitor memory usage:
// Rough guideline: Start with templates × 1.5 for cacheMaxSize// Adjust based on memory constraints and hit rate
const templateCount = 50 // Your approximate template countconst env = new Environment({ cacheMaxSize: Math.ceil(templateCount * 1.5),})Cache vs AOT
| Approach | Use Case | Performance |
|---|---|---|
| Cache | Templates with inheritance | Fast (skips lex/parse) |
| AOT | Static templates | Fastest (compiled to JS) |
For maximum performance, combine both:
import { compile, Environment } from 'binja'
// AOT for static templatesconst staticTemplates = { header: compile(await Bun.file('./partials/header.html').text()), footer: compile(await Bun.file('./partials/footer.html').text()),}
// Cached Environment for templates with inheritanceconst env = new Environment({ templates: './views', cache: true,})Framework Adapter Caching
Framework adapters (Hono, Elysia) have their own cache:
import { binja, clearCache, getCacheStats } from 'binja/hono'
app.use(binja({ root: './views', cache: true, // Enable adapter cache}))
// Access adapter cacheapp.get('/admin/cache', (c) => c.json(getCacheStats()))app.post('/admin/cache/clear', (c) => { clearCache() return c.json({ success: true })})