Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/test.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,3 @@ jobs:
run: |
npm run dev:build
npm run test:ci


1 change: 0 additions & 1 deletion docs/.vitepress/theme/custom.css
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

20 changes: 15 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ title: Nuxt Multi Cache for Nuxt 3

hero:
name: Advanced Caching for Nuxt 3
tagline: Seamless caching of components, routes and data. Dynamically define CDN cache control headers. Provides cache management API for purging items by key or using cache tags.
tagline:
Seamless caching of components, routes and data. Dynamically define CDN
cache control headers. Provides cache management API for purging items by
key or using cache tags.
image:
src: /nuxt-multi-cache.svg
alt: nuxt-multi-cache
Expand All @@ -20,14 +23,21 @@ hero:
features:
- title: Component Cache
icon: ⚡
details: Cache the rendered markup of components to reduce server render time of pages significantly.
details:
Cache the rendered markup of components to reduce server render time of
pages significantly.
- title: Route Cache
icon: 📑
details: Cache the full response of pages or custom API routes, including headers.
details:
Cache the full response of pages or custom API routes, including headers.
- title: Data Cache
icon: 💾
details: Generic composable to cache any data like external API responses or performance heavy calculations.
details:
Generic composable to cache any data like external API responses or
performance heavy calculations.
- title: CDN Headers
icon: 🌎
details: Manage Cache-Control or Cache-Tag HTTP headers for caches like Cloudflare, Fastly or Varnish.
details:
Manage Cache-Control or Cache-Tag HTTP headers for caches like Cloudflare,
Fastly or Varnish.
---
14 changes: 8 additions & 6 deletions docs/overview/migrating-from-v1.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Migrating from V1

V2 was implemented from scratch and works completely different than V1. Also
due to the breaking changes introduced in Nuxt 3 migrating the code to support
Nuxt 3 was not possible.
V2 was implemented from scratch and works completely different than V1. Also due
to the breaking changes introduced in Nuxt 3 migrating the code to support Nuxt
3 was not possible.

## $cache Plugin

Expand All @@ -13,6 +13,7 @@ The injected global `$cache` plugin is replaced by composables for each cache.
Use the `useDataCache` composable to access the data cache.

### Nuxt 2

```typescript
export default {
async asyncData({ app }) {
Expand All @@ -24,11 +25,12 @@ export default {
const response = await this.$axios.get('/api/getWeather')
await app.$cache.data.set('weather', response)
return response
}
},
}
```

### Nuxt 3

```typescript
const { data: weather } = await useAsyncData('weather', async () => {
const { value, addToCache } = await useDataCache('weather')
Expand Down Expand Up @@ -57,7 +59,7 @@ export default {
async asyncData({ app }) {
app.$cache.route.setCacheable()
app.$cache.route.addTags(['article:5', 'image:14'])
}
},
}
</script>
```
Expand Down Expand Up @@ -99,7 +101,7 @@ export default {
serverCacheKey(props) {
const variant = props.isHighlighted ? 'highlighted' : 'default'
return `${props.productId}_${variant}`
}
},
}
</script>
```
Expand Down
4 changes: 2 additions & 2 deletions docs/use-cases/short-term-route-caching.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Short Term Route Caching

For pages that change frequently you can set the max age to a lower value like
5 minutes.
For pages that change frequently you can set the max age to a lower value like 5
minutes.
10 changes: 5 additions & 5 deletions docs/use-cases/static-site-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ If you use an external cache backend like Redis you can even cache these
components across multiple SSG executions.

It's also possible to use [`<RenderCacheable>`](/features/component-cache) to
cache the markup of entire pages when used inside a layout component. This
means that if you need to regenerate the pages you only have to generate the
ones that have actually changed.
cache the markup of entire pages when used inside a layout component. This means
that if you need to regenerate the pages you only have to generate the ones that
have actually changed.

Depending on your project this can significantly improve the time it takes to
generate pages.

The benefits are noticeable the more pages and complex components you have.
This means the benefits will be barely noticeable if you only have 50 pages.
The benefits are noticeable the more pages and complex components you have. This
means the benefits will be barely noticeable if you only have 50 pages.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
"docs:dev": "vitepress dev docs --port 5000",
"docs:build": "vitepress build docs",
"docs:serve": "vitepress serve docs --port 5000",
"prettier": "prettier ./src --check",
"prettier:fix": "prettier ./src --write",
"prettier": "prettier --check ./",
"prettier:fix": "prettier --write ./",
"lint": "eslint ./src",
"lint:fix": "eslint ./src --fix",
"fake-data": "node ./scripts/fakeData.js",
Expand Down
2 changes: 1 addition & 1 deletion playground-disk/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ export default defineNuxtConfig({
},

compatibilityDate: '2025-04-28',
})
})
4 changes: 3 additions & 1 deletion playground/app/components/ReactiveTest.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<template>
<div id="counter">
<p>Count: <span id="count">{{ count }}</span></p>
<p>
Count: <span id="count">{{ count }}</span>
</p>
</div>
</template>

Expand Down
1 change: 0 additions & 1 deletion playground/app/data/users.json
Original file line number Diff line number Diff line change
Expand Up @@ -1000,4 +1000,3 @@
"registeredAt": "2022-01-28T20:11:44.527Z"
}
]

5 changes: 2 additions & 3 deletions scripts/fakeData.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ function createRandomUser() {
birthdate: faker.date.birthdate(),
company: faker.company.name(),
registeredAt: faker.date.past(),
};
}
}

async function generateUsers() {
faker.seed(43234)
const users = Array.from({ length: 100 }).map(v => createRandomUser())
const users = Array.from({ length: 100 }).map((v) => createRandomUser())
const destinationFile = path.resolve('./playground/data/users.json')

await fsp.writeFile(destinationFile, JSON.stringify(users))
Expand All @@ -28,4 +28,3 @@ async function main() {
}

main()

4 changes: 2 additions & 2 deletions src/runtime/composables/useDataCache.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { H3Event } from 'h3'
import { logger } from '../helpers/multi-cache-logger'
import type { DataCacheCallbackContext } from '../types'
import { useNuxtApp } from '#imports'
import { useRequestEvent } from '#imports'
import { debug, isServer } from '#nuxt-multi-cache/config'
import {
useDataCacheImplementation,
Expand All @@ -25,7 +25,7 @@ export async function useDataCache<T>(
return dummy
}

const event = providedEvent ?? useNuxtApp().ssrContext?.event
const event = providedEvent ?? useRequestEvent()

if (!event) {
if (debug) {
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/composables/useDataCacheCallback.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { H3Event } from 'h3'
import type { UseDataCacheCallbackCallback } from '../server/utils/useDataCacheCallback'
import { useNuxtApp } from '#imports'
import { useRequestEvent } from '#imports'
import { logger } from '../helpers/multi-cache-logger'
import { debug, isServer } from '#nuxt-multi-cache/config'
import type { UseDataCacheOptions } from '../shared/useDataCache'
Expand All @@ -13,7 +13,7 @@ export async function useDataCacheCallback<T>(
options?: UseDataCacheOptions,
): Promise<T> {
if (isServer) {
const event = providedEvent || useNuxtApp().ssrContext?.event
const event = providedEvent ?? useRequestEvent()

if (event) {
return useDataCacheCallbackImplementation(key, cb, event, options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,21 @@ type ClientBundle = {
/**
* The strings to check.
*/
const STRINGS = ['consola']
const STRINGS = [
// Must not be included because the build plugin overrides the entire file exporting these.
'consola',
'serverOptions',

// Should never be in the client bundle because it's noop on the client.
'useCDNHeaders',
'useRouteCache',
'bubbleCacheability(',

// Random strings that only appear in server code.
'Failed to get component cache item',
'Failed to get SSR context',
'No H3Event provided',
]

/**
* Test that none of the defined strings appears in any of the generated client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@ function buildEvent(): H3Event {
} as H3Event
}

let returnEvent = true

vi.mock('#imports', () => {
return {
useRequestEvent: () => {
return buildEvent()
get useRequestEvent() {
return () => {
if (returnEvent) {
return buildEvent()
}
return undefined
}
},
useRuntimeConfig: () => {
return {
Expand Down Expand Up @@ -55,6 +62,7 @@ vi.mock('#nuxt-multi-cache/config', () => {
describe('useCDNHeaders composable', () => {
beforeEach(() => {
isServerValue = false
returnEvent = true
})
test('Does not call callback in client', () => {
const params = {
Expand Down Expand Up @@ -105,6 +113,7 @@ describe('useCDNHeaders composable', () => {

test('Does not call callback if event is missing.', () => {
isServerValue = true
returnEvent = false

const params = {
cb() {},
Expand Down
18 changes: 7 additions & 11 deletions test/composables/useRouteCache.nuxt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mockNuxtImport('useRuntimeConfig', () => {
})

let isServerValue = false
let returnEvent = true

vi.mock('#nuxt-multi-cache/config', () => {
return {
Expand Down Expand Up @@ -58,24 +59,18 @@ function buildEvent(): H3Event {
} as H3Event
}

vi.mock('#imports', () => {
return {
useRequestEvent: () => {
mockNuxtImport('useRequestEvent', () => {
return () => {
if (returnEvent) {
return buildEvent()
},
useRuntimeConfig: () => {
return {
multiCache: {
data: true,
},
}
},
}
}
})

describe('useRouteCache composable', () => {
beforeEach(() => {
isServerValue = false
returnEvent = true
})
test('Does not call callback in client', () => {
const params = {
Expand Down Expand Up @@ -126,6 +121,7 @@ describe('useRouteCache composable', () => {

test('Does not call callback if event is missing.', () => {
isServerValue = true
returnEvent = false

const params = {
cb() {},
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
16 changes: 11 additions & 5 deletions vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,34 @@ import path from 'path'

const alias: Record<string, string> = {
'~': path.resolve(__dirname),
'#nuxt-multi-cache/config': path.resolve(__dirname, './.nuxt/nuxt-multi-cache/config.js'),
'#nuxt-multi-cache/server-options': path.resolve(__dirname, './.nuxt/nuxt-multi-cache/server-options.js')
'#nuxt-multi-cache/config': path.resolve(
__dirname,
'./.nuxt/nuxt-multi-cache/config.js',
),
'#nuxt-multi-cache/server-options': path.resolve(
__dirname,
'./.nuxt/nuxt-multi-cache/server-options.js',
),
'#imports': path.resolve(__dirname, './.nuxt/imports.mjs'),
}

export default defineConfig({
test: {

projects: [
{
test: {
name: 'unit',
include: ['test/**/*.{e2e,node}.spec.ts'],
environment: 'node',
alias
alias,
},
},
await defineVitestProject({
test: {
name: 'nuxt',
include: ['test/**/*.nuxt.spec.ts'],
environment: 'nuxt',
alias
alias,
},
}),
],
Expand Down