Incremental Static Regeneration (ISR)
Core Concepts and Configuration of ISR
ISR Core Mechanism:
// packages/nuxt/src/core/runtime/nitro/renderer.ts
export class ISRRenderer {
private revalidateMap: Map<string, number> = new Map()
// Get page revalidate configuration
getRevalidateConfig(routePath: string): number | false {
const pageConfig = this.getPageConfig(routePath)
return pageConfig?.revalidate ?? this.globalRevalidate
}
// Execute ISR
async handleISR(routePath: string, renderFn: () => Promise<string>) {
const revalidate = this.getRevalidateConfig(routePath)
if (revalidate === false) {
// Pure SSR mode
return renderFn()
}
// Check cache
const cached = await this.getCache(routePath)
if (cached && !this.isCacheExpired(cached, revalidate)) {
return cached.html
}
// Perform re-rendering
const html = await renderFn()
// Update cache
await this.setCache(routePath, {
html,
timestamp: Date.now()
})
return html
}
}revalidate Parameter Configuration Example:
// nuxt.config.js
export default {
nitro: {
isr: {
// Global default revalidate time (seconds)
revalidate: 60
}
},
pages: {
'/blog/:id': {
// Specific configuration for dynamic routes
revalidate: 30 // 30 seconds revalidation
},
'/about': {
revalidate: false // Disable ISR, pure SSR mode
}
}
}ISR Implementation for Dynamic Routes
Dynamic Route Parameter Handling:
// packages/nuxt/src/core/runtime/nitro/route.ts
export class DynamicRouteHandler {
// Parse dynamic route parameters
parseDynamicParams(routePattern: string, requestPath: string) {
const paramNames = this.extractParamNames(routePattern)
const regex = this.convertToRegex(routePattern)
const match = requestPath.match(regex)
if (!match) throw new Error('Route not matched')
return paramNames.reduce((params, name, index) => {
params[name] = decodeURIComponent(match[index + 1])
return params
}, {} as Record<string, string>)
}
// Generate cache key for dynamic routes
generateCacheKey(routePath: string, params: Record<string, string>) {
const sortedParams = Object.keys(params)
.sort()
.map(key => `${key}=${params[key]}`)
.join('&')
return `${routePath}?${sortedParams}`
}
}asyncData and revalidate Integration:
// packages/nuxt/src/core/runtime/nitro/data-fetching.ts
export async function handleAsyncData(
context: NuxtContext,
routePath: string,
asyncDataFn?: () => Promise<any>
) {
// 1. Check ISR configuration
const revalidate = context.nitro.isr.getRevalidateConfig(routePath)
// 2. Execute data fetching
let data = {}
if (asyncDataFn) {
data = await asyncDataFn(context)
}
// 3. ISR handling
if (revalidate !== false) {
const cacheKey = context.nitro.isr.generateCacheKey(routePath, context.params)
await context.nitro.isr.handleISR(cacheKey, async () => {
// Re-execute asyncData
return asyncDataFn ? await asyncDataFn(context) : {}
})
}
return data
}Performance Optimization and Cache Strategies for ISR
Multi-Level Cache Implementation:
// packages/nuxt/src/core/runtime/nitro/cache.ts
export class MultiLevelCache {
// Memory cache (fast access)
private memoryCache = new Map<string, CacheItem>()
// Persistent cache (disk/database)
private persistentCache: PersistentCache
async get(key: string): Promise<CacheItem | null> {
// 1. Check memory cache
if (this.memoryCache.has(key)) {
const item = this.memoryCache.get(key)!
if (!this.isExpired(item)) {
return item
}
this.memoryCache.delete(key)
}
// 2. Check persistent cache
const persistentItem = await this.persistentCache.get(key)
if (persistentItem && !this.isExpired(persistentItem)) {
// Backfill memory cache
this.memoryCache.set(key, persistentItem)
return persistentItem
}
return null
}
async set(key: string, value: CacheItem) {
// Set memory cache (with TTL)
this.memoryCache.set(key, value)
setTimeout(() => this.memoryCache.delete(key), value.ttl)
// Asynchronously write to persistent cache
this.persistentCache.set(key, value)
}
}Cache Warming Strategy:
// packages/nuxt/src/core/runtime/nitro/warmup.ts
export class CacheWarmer {
async warmupRoutes(routes: string[]) {
// Warm up all routes in parallel
await Promise.all(
routes.map(async route => {
// 1. Get route configuration
const config = this.getRouteConfig(route)
// 2. Perform rendering
const html = await this.renderRoute(route, config)
// 3. Populate cache
await this.cache.set(
route,
{
html,
timestamp: Date.now(),
ttl: config.revalidate * 1000
}
)
})
)
}
}Comparison and Selection Between ISR and SSR
Decision Matrix:
| Feature | ISR | SSR |
|---|---|---|
| First Load Performance | Slower (initial generation required) | Fast (direct generation) |
| Subsequent Request Performance | Very Fast (cache hit) | Moderate (generated each time) |
| Server Load | Low (no processing on cache hit) | High (processed every request) |
| Data Freshness | Configurable (via revalidate) | Real-time (latest data per request) |
| Use Cases | Relatively stable content | Highly dynamic content |
| Implementation Complexity | Moderate (requires cache logic) | Simple (direct rendering) |
Selection Guidelines:
- Choose ISR when:
- Content updates infrequently (e.g., blog posts)
- Brief data delays are acceptable (configured via revalidate)
- Server load reduction is needed
- Choose SSR when:
- Content is highly dynamic (e.g., user dashboards)
- Real-time data is required
- Higher server load is acceptable
Security and Access Control for ISR
Permission-Based Cache Strategy:
// packages/nuxt/src/core/runtime/nitro/auth.ts
export class AuthAwareCache {
async getWithAuth(key: string, getUser: () => Promise<User | null>) {
const user = await getUser()
const cacheKey = this.getUserSpecificKey(key, user)
// Get cache
const cached = await this.cache.get(cacheKey)
if (cached && !this.isExpired(cached)) {
return cached.html
}
// Perform re-rendering
const html = await this.renderWithAuth(key, user)
// Update cache
await this.cache.set(cacheKey, {
html,
timestamp: Date.now(),
ttl: this.getTTLForUser(user)
})
return html
}
private getUserSpecificKey(baseKey: string, user: User | null) {
if (!user) return `anon:${baseKey}`
return `user:${user.id}:${baseKey}`
}
private getTTLForUser(user: User | null): number {
if (!user) return 60 // Short TTL for anonymous users
if (user.role === 'admin') return 0 // Disable cache for admins
return 300 // 5-minute TTL for regular users
}
}Sensitive Data Protection:
// packages/nuxt/src/core/runtime/nitro/sensitive-data.ts
export function protectSensitiveData(data: any, context: NuxtContext) {
// 1. Check if data contains sensitive fields
if (this.hasSensitiveFields(data)) {
// 2. Check user permissions
if (!this.hasPermission(context.user, 'view_sensitive_data')) {
// 3. Filter sensitive data
return this.filterSensitiveFields(data)
}
}
return data
}
// Integrate with ISR process
export async function handleISRSensitiveRoute(
routePath: string,
renderFn: () => Promise<string>,
context: NuxtContext
) {
// 1. Perform rendering (server-side)
const html = await renderFn()
// 2. Filter sensitive data during client-side hydration
if (context.isClient) {
return this.protectSensitiveData(html, context)
}
// 3. Fully filter during server-side caching
return this.protectSensitiveData(html, context)
}Advanced Module System
Nuxt Module Development and Integration
Module Development Template:
// Example custom module
export default defineNuxtModule({
meta: {
name: 'my-module',
configKey: 'myModule'
},
// Default configuration
defaults: {
featureFlag: true,
apiEndpoint: '/api'
},
// Hook functions
hooks: {
'app:created'(nuxtApp) {
// Execute when app is created
},
'pages:extend'(pages) {
// Extend page routes
}
},
// Runtime configuration
runtimeConfig: {
public: {
// Client-accessible configuration
featureFlag: true
}
},
// Setup method
setup(options, nuxt) {
// 1. Add runtime configuration
nuxt.options.runtimeConfig.myModule = options
// 2. Extend Vue prototype
nuxt.hook('vue:setup', (app) => {
app.config.globalProperties.$myModule = {
getData() { /* ... */ }
}
})
// 3. Register plugin
nuxt.hook('nitro:config', (config) => {
config.plugins.push('./runtime/plugin')
})
}
})Module Integration Process:
// packages/nuxt/src/core/modules/index.ts
export async function loadModules(nuxt: Nuxt) {
// 1. Load built-in modules
await loadBuiltInModules(nuxt)
// 2. Load project modules
const modulePaths = await findModulePaths(nuxt.options.rootDir)
for (const path of modulePaths) {
await loadModule(path, nuxt)
}
// 3. Apply module configurations
applyModuleConfigs(nuxt)
// 4. Execute module hooks
await runModuleHooks('init', nuxt)
}Module Configuration and Lifecycle
Module Configuration Merge Strategy:
// packages/nuxt/src/core/config/modules.ts
export function mergeModuleConfigs(
defaultConfig: Record<string, any>,
userConfig: Record<string, any>
) {
// 1. Deep merge configurations
const merged = deepMerge(defaultConfig, userConfig)
// 2. Handle environment variable overrides
if (process.env.NUXT_MODULE_OPTIONS) {
const envOverrides = JSON.parse(process.env.NUXT_MODULE_OPTIONS)
return deepMerge(merged, envOverrides)
}
return merged
}
// Module lifecycle management
export class ModuleManager {
private modules: Map<string, NuxtModule> = new Map()
async register(module: NuxtModule) {
// 1. Initialize module
await module.setup()
// 2. Register hooks
this.registerHooks(module)
// 3. Store module reference
this.modules.set(module.meta.name, module)
}
async runHook(hookName: string, ...args: any[]) {
// Execute all module hooks in order
for (const module of this.modules.values()) {
if (module.hooks[hookName]) {
await module.hooks[hookName](...args)
}
}
}
}Differences and Relationships Between Modules and Plugins
Modules vs. Plugins Comparison Table:
| Feature | Module (Nuxt Module) | Plugin (Plugin) |
|---|---|---|
| Purpose | Extend Nuxt core functionality | Extend Vue app functionality |
| Loading Time | During Nuxt initialization | During Vue app creation |
| Configuration Capability | Supports full configuration system | Typically simple configuration |
| Integration Depth | Access to Nuxt internal APIs | Mainly access to Vue context |
| Typical Use Cases | Add server middleware, build tools | Add Vue directives, global methods |
| Dependency Management | Automatically handles module dependencies | Requires manual dependency management |
Plugin Integration Example:
// Register plugin in a module
export default defineNuxtModule({
setup(options, nuxt) {
nuxt.hook('vue:setup', (app) => {
// Register Vue plugin
app.use(MyVuePlugin, { /* Plugin options */ })
})
// Register Nitro plugin (server-side)
nuxt.hook('nitro:config', (config) => {
config.plugins.push('./server-plugin')
})
}
})Module Security and Access Control
Module Sandbox Mechanism:
// packages/nuxt/src/core/security/module-sandbox.ts
export class ModuleSandbox {
private allowedAPIs: Set<string> = new Set([
'console.log',
'setTimeout',
// ...other safe APIs
])
execute(code: string, context: Record<string, any>) {
// 1. Create proxy environment
const proxy = this.createProxy(context)
// 2. Safely execute
try {
// Use Function constructor instead of eval
const fn = new Function(...Object.keys(proxy), code)
return fn(...Object.values(proxy))
} catch (error) {
this.handleSecurityError(error)
}
}
private createProxy(context: Record<string, any>) {
return new Proxy(context, {
get(target, prop) {
const fullPath = `globalThis.${prop}`
if (this.allowedAPIs.has(fullPath)) {
return target[prop]
}
throw new Error(`Access to ${prop} is restricted`)
}
})
}
}Module Permission Verification:
// packages/nuxt/src/core/security/module-auth.ts
export class ModuleAuth {
async verifyModuleAccess(moduleName: string, user: User | null) {
// 1. Get module metadata
const meta = this.getModuleMeta(moduleName)
// 2. Check permission requirements
if (meta.requiredRole) {
if (!user || !this.hasRole(user, meta.requiredRole)) {
throw new Error(`Access to ${moduleName} requires ${meta.requiredRole} role`)
}
}
// 3. Check feature flags
if (meta.featureFlag && !this.isFeatureEnabled(meta.featureFlag)) {
throw new Error(`Feature ${meta.featureFlag} is disabled`)
}
return true
}
}Module Performance Optimization
Module Lazy Loading Implementation:
// packages/nuxt/src/core/modules/lazy.ts
export class LazyModuleLoader {
private loadedModules = new Set<string>()
async load(moduleName: string) {
// 1. Check if already loaded
if (this.loadedModules.has(moduleName)) {
return
}
// 2. Dynamically import module
try {
const module = await import(`../modules/${moduleName}`)
// 3. Initialize module
await module.default.setup()
// 4. Mark as loaded
this.loadedModules.add(moduleName)
} catch (error) {
console.error(`Failed to load module ${moduleName}:`, error)
}
}
// Route-based module preloading
setupRouteBasedPreloading(nuxt: Nuxt) {
nuxt.hook('pages:extend', (pages) => {
pages.forEach(page => {
page.middleware.push(async (context) => {
// Preload modules potentially needed for this route
const requiredModules = this.detectRequiredModules(page.path)
await Promise.all(requiredModules.map(m => this.load(m)))
})
})
})
}
}Module Bundling Optimization:
// packages/nuxt/src/build/modules-optimizer.ts
export class ModuleOptimizer {
optimize(modules: NuxtModule[]) {
// 1. Analyze module dependency graph
const dependencyGraph = this.buildDependencyGraph(modules)
// 2. Identify parallel-loadable modules
const parallelGroups = this.identifyParallelGroups(dependencyGraph)
// 3. Generate optimized loading order
const optimizedOrder = this.generateOptimizedOrder(parallelGroups)
// 4. Apply code splitting
this.applyCodeSplitting(optimizedOrder)
return {
loadingOrder: optimizedOrder,
codeSplitConfig: this.getCodeSplitConfig()
}
}
private buildDependencyGraph(modules: NuxtModule[]) {
// Build module dependency graph
// ...
}
}Image and Font Optimization
Advanced Configuration of Nuxt Image Module
Advanced Image Configuration Example:
// nuxt.config.js
export default {
image: {
// Custom loader
domains: ['images.example.com'],
provider: 'cloudinary',
providerOptions: {
cloudName: 'my-cloud',
apiKey: '12345'
},
// Image quality settings
quality: 80,
formats: ['webp', 'jpeg'],
// Responsive breakpoints
screens: {
xs: 320,
sm: 640,
md: 1024,
lg: 1280,
xl: 1920
},
// Blur placeholder
presets: {
avatar: {
modifiers: {
format: 'png',
width: 50,
height: 50,
quality: 50
}
}
}
}
}Dynamic Image Processing Source Code:
// packages/nuxt/image/src/runtime/component.ts
export default defineComponent({
props: {
src: { type: String, required: true },
width: { type: [Number, String], default: null },
height: { type: [Number, String], default: null },
quality: { type: Number, default: null },
format: { type: String, default: null }
},
async setup(props) {
// 1. Generate optimized image URL
const optimizedUrl = await generateOptimizedUrl({
src: props.src,
width: props.width,
height: props.height,
quality: props.quality || nuxtImageConfig.quality,
format: props.format || nuxtImageConfig.formats[0]
})
// 2. Load blur placeholder
const placeholderUrl = await generatePlaceholderUrl(props.src)
return {
optimizedUrl,
placeholderUrl
}
}
})Font Optimization Strategies
Google Fonts Optimization Integration:
// nuxt.config.js
export default {
googleFonts: {
families: {
Roboto: [400, 700],
'Open+Sans': true
},
display: 'swap',
preload: true,
inject: true
}
}Custom Font Loading Optimization:
// packages/nuxt/src/core/features/fonts.ts
export class FontOptimizer {
async loadFonts() {
// 1. Detect system fonts
const systemFonts = this.detectSystemFonts()
// 2. Load custom fonts (with preloading)
if (this.hasCustomFonts()) {
await this.preloadCriticalFonts()
this.loadNonCriticalFonts()
}
// 3. Apply font display strategy
this.setFontDisplayStrategy('swap')
}
private preloadCriticalFonts() {
// Use <link rel="preload"> to preload critical fonts
const criticalFonts = this.getCriticalFonts()
criticalFonts.forEach(font => {
const link = document.createElement('link')
link.rel = 'preload'
link.href = font.url
link.as = 'font'
link.type = 'font/woff2'
link.crossOrigin = 'anonymous'
document.head.appendChild(link)
})
}
}Performance Optimization for Fonts and Images
Comprehensive Performance Optimization Strategies:
// packages/nuxt/src/core/performance/optimization.ts
export class AssetOptimizer {
// Image optimization
optimizeImages() {
// 1. Generate responsive images
this.generateResponsiveImages()
// 2. Lazy load off-screen images
this.lazyLoadOffscreenImages()
// 3. Enable progressive image loading
this.enableProgressiveLoading()
}
// Font optimization
optimizeFonts() {
// 1. Font subsetting
this.subsetFontCharacters()
// 2. Font display strategy
this.setFontDisplayStrategy('swap')
// 3. Preload critical fonts
this.preloadCriticalFonts()
}
// Comprehensive optimization
runFullOptimization() {
// 1. Critical rendering path optimization
this.optimizeCriticalAssets()
// 2. Resource priority adjustment
this.adjustResourcePriority()
// 3. Configure cache policies
this.configureCachePolicies()
}
}Cache Strategies for Fonts and Images
Smart Cache Implementation:
// packages/nuxt/src/core/cache/assets.ts
export class AssetCache {
// Memory cache (fast access)
private memoryCache = new Map<string, CachedAsset>()
// Service worker cache
private serviceWorkerCache: Cache
async get(url: string): Promise<CachedAsset | null> {
// 1. Check memory cache
if (this.memoryCache.has(url)) {
const cached = this.memoryCache.get(url)!
if (!this.isExpired(cached)) {
return cached
}
this.memoryCache.delete(url)
}
// 2. Check service worker cache
if (this.serviceWorkerCache) {
const response = await this.serviceWorkerCache.match(url)
if (response) {
const cached = await this.parseCachedResponse(response)
this.memoryCache.set(url, cached) // Backfill memory cache
return cached
}
}
return null
}
async set(url: string, asset: CachedAsset) {
// 1. Set memory cache
this.memoryCache.set(url, asset)
setTimeout(() => this.memoryCache.delete(url), asset.ttl)
// 2. Update service worker cache
if (this.serviceWorkerCache) {
const response = await this.createCacheResponse(asset)
await this.serviceWorkerCache.put(url, response)
}
}
}Cache Invalidation Strategy:
// packages/nuxt/src/core/cache/invalidation.ts
export class CacheInvalidator {
// Content hash-based cache invalidation
async invalidateByContentHash() {
// 1. Calculate current content hash
const currentHash = await this.calculateContentHash()
// 2. Compare with last cached hash
const lastHash = await this.getLastCacheHash()
if (currentHash !== lastHash) {
// 3. Clear old cache
await this.clearCache()
// 4. Store new hash
await this.storeCacheHash(currentHash)
}
}
// Time-based cache invalidation
async invalidateByTime(ttl: number) {
const lastUpdated = await this.getLastUpdatedTime()
if (Date.now() - lastUpdated > ttl * 1000) {
await this.clearCache()
await this.updateLastUpdatedTime()
}
}
}Security and Compatibility for Fonts and Images
Secure Loading Strategy:
// packages/nuxt/src/core/security/assets.ts
export class AssetSecurity {
// Secure image loading
async loadSecureImage(url: string) {
// 1. Validate URL source
if (!this.isAllowedDomain(url)) {
throw new Error('Image domain not allowed')
}
// 2. Get content security policy
const csp = this.getContentSecurityPolicy()
// 3. Securely load image
return new Promise((resolve, reject) => {
const img = new Image()
img.crossOrigin = 'anonymous'
img.src = url
img.onload = () => resolve(img)
img.onerror = () => reject(new Error('Failed to load image'))
// Apply CSP restrictions
if (csp) {
this.applyCSPToImage(img, csp)
}
})
}
// Secure font loading
async loadSecureFont(url: string) {
// 1. Validate font source
if (!this.isAllowedFontDomain(url)) {
throw new Error('Font domain not allowed')
}
// 2. Check font MIME type
const mimeType = await this.detectMimeType(url)
if (!this.isAllowedFontType(mimeType)) {
throw new Error('Font type not allowed')
}
// 3. Securely load font
return this.loadFontWithCSP(url)
}
}Compatibility Handling:
// packages/nuxt/src/core/compatibility/assets.ts
export class AssetCompatibility {
// Image format compatibility handling
async getCompatibleImageFormat(preferredFormats: string[]) {
// 1. Detect browser-supported formats
const supportedFormats = await this.detectSupportedFormats()
// 2. Find best matching format
for (const format of preferredFormats) {
if (supportedFormats.includes(format)) {
return format
}
}
// 3. Fallback to default format
return 'jpeg'
}
// Font loading compatibility handling
async loadCompatibleFont(fontConfig: FontConfig) {
// 1. Detect browser font support
const fontSupport = await this.detectFontSupport()
// 2. Adjust font configuration
const adjustedConfig = this.adjustFontConfig(fontConfig, fontSupport)
// 3. Load adjusted font
return this.loadFont(adjustedConfig)
}
}



