mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-13 09:33:54 +00:00
perf(nuxt): use LRU prerender cache and limit cached items (#22465)
This commit is contained in:
parent
2f065184d2
commit
3722325220
@ -273,6 +273,12 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
// Init nitro
|
// Init nitro
|
||||||
const nitro = await createNitro(nitroConfig)
|
const nitro = await createNitro(nitroConfig)
|
||||||
|
|
||||||
|
// Set prerender-only options
|
||||||
|
nitro.options._config.storage ||= {}
|
||||||
|
nitro.options._config.storage['internal:nitro:prerender'] = { driver: 'memory' }
|
||||||
|
nitro.options._config.storage['internal:nitro:prerender:island'] = { driver: 'lruCache', max: 1000 }
|
||||||
|
nitro.options._config.storage['internal:nitro:prerender:payload'] = { driver: 'lruCache', max: 1000 }
|
||||||
|
|
||||||
// Expose nitro to modules and kit
|
// Expose nitro to modules and kit
|
||||||
nuxt._nitro = nitro
|
nuxt._nitro = nitro
|
||||||
await nuxt.callHook('nitro:init', nitro)
|
await nuxt.callHook('nitro:init', nitro)
|
||||||
|
@ -17,7 +17,7 @@ import { renderToString as _renderToString } from 'vue/server-renderer'
|
|||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
import { renderSSRHead } from '@unhead/ssr'
|
import { renderSSRHead } from '@unhead/ssr'
|
||||||
|
|
||||||
import { defineRenderHandler, getRouteRules, useRuntimeConfig } from '#internal/nitro'
|
import { defineRenderHandler, getRouteRules, useRuntimeConfig, useStorage } from '#internal/nitro'
|
||||||
import { useNitroApp } from '#internal/nitro/app'
|
import { useNitroApp } from '#internal/nitro/app'
|
||||||
|
|
||||||
import type { Link, Script } from '@unhead/vue'
|
import type { Link, Script } from '@unhead/vue'
|
||||||
@ -155,9 +155,18 @@ const getSPARenderer = lazyCachedFunction(async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const payloadCache = process.env.prerender ? useStorage('internal:nuxt:prerender:payload') : null
|
||||||
|
const islandCache = process.env.prerender ? useStorage('internal:nuxt:prerender:island') : null
|
||||||
|
const islandPropCache = process.env.prerender ? useStorage('internal:nuxt:prerender:island-props') : null
|
||||||
|
|
||||||
async function getIslandContext (event: H3Event): Promise<NuxtIslandContext> {
|
async function getIslandContext (event: H3Event): Promise<NuxtIslandContext> {
|
||||||
// TODO: Strict validation for url
|
// TODO: Strict validation for url
|
||||||
const url = event.node.req.url?.substring('/__nuxt_island'.length + 1) || ''
|
let url = event.node.req.url || ''
|
||||||
|
if (process.env.prerender && event.node.req.url && await islandPropCache!.hasItem(event.node.req.url)) {
|
||||||
|
// rehydrate props from cache so we can rerender island if cache does not have it any more
|
||||||
|
url = await islandPropCache!.getItem(event.node.req.url) as string
|
||||||
|
}
|
||||||
|
url = url.substring('/__nuxt_island'.length + 1) || ''
|
||||||
const [componentName, hashId] = url.split('?')[0].split('_')
|
const [componentName, hashId] = url.split('?')[0].split('_')
|
||||||
|
|
||||||
// TODO: Validate context
|
// TODO: Validate context
|
||||||
@ -175,8 +184,6 @@ async function getIslandContext (event: H3Event): Promise<NuxtIslandContext> {
|
|||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
const PAYLOAD_CACHE = (process.env.NUXT_PAYLOAD_EXTRACTION && process.env.prerender) ? new Map() : null // TODO: Use LRU cache
|
|
||||||
const ISLAND_CACHE = (process.env.NUXT_COMPONENT_ISLANDS && process.env.prerender) ? new Map() : null // TODO: Use LRU cache
|
|
||||||
const PAYLOAD_URL_RE = process.env.NUXT_JSON_PAYLOADS ? /\/_payload(\.[a-zA-Z0-9]+)?.json(\?.*)?$/ : /\/_payload(\.[a-zA-Z0-9]+)?.js(\?.*)?$/
|
const PAYLOAD_URL_RE = process.env.NUXT_JSON_PAYLOADS ? /\/_payload(\.[a-zA-Z0-9]+)?.json(\?.*)?$/ : /\/_payload(\.[a-zA-Z0-9]+)?.js(\?.*)?$/
|
||||||
const ROOT_NODE_REGEX = new RegExp(`^<${appRootTag} id="${appRootId}">([\\s\\S]*)</${appRootTag}>$`)
|
const ROOT_NODE_REGEX = new RegExp(`^<${appRootTag} id="${appRootId}">([\\s\\S]*)</${appRootTag}>$`)
|
||||||
|
|
||||||
@ -206,8 +213,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
? await getIslandContext(event)
|
? await getIslandContext(event)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
if (process.env.prerender && islandContext && ISLAND_CACHE!.has(event.node.req.url)) {
|
if (process.env.prerender && islandContext && event.node.req.url && await islandCache!.hasItem(event.node.req.url)) {
|
||||||
return ISLAND_CACHE!.get(event.node.req.url)
|
return islandCache!.getItem(event.node.req.url) as Promise<Partial<RenderResponse>>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request url
|
// Request url
|
||||||
@ -218,8 +225,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
if (isRenderingPayload) {
|
if (isRenderingPayload) {
|
||||||
url = url.substring(0, url.lastIndexOf('/')) || '/'
|
url = url.substring(0, url.lastIndexOf('/')) || '/'
|
||||||
event.node.req.url = url
|
event.node.req.url = url
|
||||||
if (process.env.prerender && PAYLOAD_CACHE!.has(url)) {
|
if (process.env.prerender && await payloadCache!.hasItem(url)) {
|
||||||
return PAYLOAD_CACHE!.get(url)
|
return payloadCache!.getItem(url) as Promise<Partial<RenderResponse>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +292,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
if (isRenderingPayload) {
|
if (isRenderingPayload) {
|
||||||
const response = renderPayloadResponse(ssrContext)
|
const response = renderPayloadResponse(ssrContext)
|
||||||
if (process.env.prerender) {
|
if (process.env.prerender) {
|
||||||
PAYLOAD_CACHE!.set(url, response)
|
await payloadCache!.setItem(url, response)
|
||||||
}
|
}
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
@ -294,7 +301,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
// Hint nitro to prerender payload for this route
|
// Hint nitro to prerender payload for this route
|
||||||
appendResponseHeader(event, 'x-nitro-prerender', joinURL(url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js'))
|
appendResponseHeader(event, 'x-nitro-prerender', joinURL(url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js'))
|
||||||
// Use same ssr context to generate payload for this route
|
// Use same ssr context to generate payload for this route
|
||||||
PAYLOAD_CACHE!.set(withoutTrailingSlash(url), renderPayloadResponse(ssrContext))
|
await payloadCache!.setItem(withoutTrailingSlash(url), renderPayloadResponse(ssrContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.NUXT_INLINE_STYLES && !islandContext) {
|
if (process.env.NUXT_INLINE_STYLES && !islandContext) {
|
||||||
@ -422,7 +429,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
}
|
}
|
||||||
} satisfies RenderResponse
|
} satisfies RenderResponse
|
||||||
if (process.env.prerender) {
|
if (process.env.prerender) {
|
||||||
ISLAND_CACHE!.set(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, response)
|
await islandCache!.setItem(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, response)
|
||||||
|
await islandPropCache!.setItem(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, event.node.req.url!)
|
||||||
}
|
}
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user