Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/nuxtPublic

feat(nuxt): addrefresh option to useCookie#33814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
riabinkovihor wants to merge7 commits intonuxt:main
base:main
Choose a base branch
Loading
fromriabinkovihor:feat/cookie-refresh-option
Open
Show file tree
Hide file tree
Changes from6 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

Some comments aren't visible on the classic Files Changed page.

24 changes: 24 additions & 0 deletionsdocs/4.api/2.composables/use-cookie.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,7 @@ export interface CookieOptions<T = any> extends Omit<CookieSerializeOptions & Co
default?: () => T | Ref<T>
watch?: boolean | 'shallow'
readonly?: boolean
refresh?: boolean
}

export interface CookieRef<T> extends Ref<T> {}
Expand All@@ -60,6 +61,7 @@ Most of the options will be directly passed to the [cookie](https://github.com/j
| `encode` | `(value: T) => string` | `JSON.stringify` + `encodeURIComponent` | Custom function to encode the cookie value. Since the value of a cookie has a limited character set (and must be a simple string), this function can be used to encode a value into a string suited for a cookie's value. |
| `default` | `() => T \| Ref<T>` | `undefined` | Function returning the default value if the cookie does not exist. The function can also return a `Ref`. |
| `watch` | `boolean \| 'shallow'` | `true` | Whether to watch for changes and update the cookie. `true` for deep watch, `'shallow'` for shallow watch, i.e. data changes for only top level properties, `false` to disable. <br/> **Note:** Refresh `useCookie` values manually when a cookie has changed with [`refreshCookie`](/docs/4.x/api/utils/refresh-cookie). |
| `refresh` | `boolean` | `false` | If `true`, the cookie expiration will be refreshed on every explicit write, even if the value itself hasn’t changed. |
| `readonly` | `boolean` | `false` | If `true`, disables writing to the cookie. |
| `maxAge` | `number` | `undefined` | Max age in seconds for the cookie, i.e. the value for the [`Max-Age` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.2). The given number will be converted to an integer by rounding down. By default, no maximum age is set. |
| `expires` | `Date` | `undefined` | Expiration date for the cookie. By default, no expiration is set. Most clients will consider this a "non-persistent cookie" and will delete it on a condition like exiting a web browser application. <br/> **Note:** The [cookie storage model specification](https://datatracker.ietf.org/doc/html/rfc6265#section-5.3) states that if both `expires` and `maxAge` is set, then `maxAge` takes precedence, but not all clients may obey this, so if both are set, they should point to the same date and time! <br/>If neither of `expires` and `maxAge` is set, the cookie will be session-only and removed when the user closes their browser. |
Expand DownExpand Up@@ -163,6 +165,28 @@ function save () {
</template>
```

### Refreshing Cookies

```vue
<script setup lang="ts">
const session = useCookie(
'session', {
maxAge: 60 * 60, // 1 hour
refresh: true,
default: () => 'active',
})

// Even if the value does not change,
// the cookie expiration will be refreshed
// every time the setter is called
session.value = 'active'
</script>

<template>
<div>Session: {{ session }}</div>
</template>
```

### Cookies in API Routes

You can use `getCookie` and `setCookie` from [`h3`](https://github.com/h3js/h3) package to set cookies in server API routes.
Expand Down
58 changes: 54 additions & 4 deletionspackages/nuxt/src/app/composables/cookie.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,6 +21,17 @@ export interface CookieOptions<T = any> extends _CookieOptions {
default?: () => T | Ref<T>
watch?: boolean | 'shallow'
readonly?: boolean

/**
* Refresh cookie expiration even when the value remains unchanged.
*
* By default, a cookie is only rewritten when its value changes.
* When `refresh` is set to `true`, the cookie will be re-written
* on every assignment, extending its expiration.
*
* @default false
*/
refresh?: boolean
}

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
Expand All@@ -31,6 +42,7 @@ const CookieDefaults = {
watch: true,
decode: val => destr(decodeURIComponent(val)),
encode: val => encodeURIComponent(typeof val === 'string' ? val : JSON.stringify(val)),
refresh: false,
} satisfies CookieOptions<any>

// we use globalThis to avoid crashes in web workers
Expand All@@ -57,10 +69,10 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
const shouldSetInitialClientCookie = import.meta.client && (hasExpired || cookies[name] === undefined || cookies[name] === null)
const cookieValue = klona(hasExpired ? undefined : (cookies[name] as any) ?? opts.default?.())

// use a custom ref to expire the cookie on client side otherwise usebasic ref
// use a custom ref to expire the cookie on client side otherwise usecookieServerRef
const cookie = import.meta.client && delay && !hasExpired
? cookieRef<T | undefined>(cookieValue, delay, opts.watch && opts.watch !== 'shallow')
:ref<T | undefined>(cookieValue)
:cookieServerRef<T | undefined>(name,cookieValue)

if (import.meta.dev && hasExpired) {
console.warn(`[nuxt] not setting cookie \`${name}\` as it has already expired.`)
Expand DownExpand Up@@ -131,7 +143,7 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
if (opts.watch) {
watch(cookie, () => {
if (watchPaused) { return }
callback()
callback(opts.refresh)
},
{ deep: opts.watch !== 'shallow' })
}
Expand All@@ -142,7 +154,18 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
} else if (import.meta.server) {
const nuxtApp = useNuxtApp()
const writeFinalCookieValue = () => {
if (opts.readonly || isEqual(cookie.value, cookies[name])) { return }
const valueIsSame = isEqual(cookie.value, cookies[name])

if (
opts.readonly
|| (valueIsSame && !opts.refresh)
) { return }

nuxtApp._cookiesChanged ||= {}
if (valueIsSame && opts.refresh && !nuxtApp._cookiesChanged[name]) {
return
}

nuxtApp._cookies ||= {}
if (name in nuxtApp._cookies) {
// do not append a second `set-cookie` header
Expand DownExpand Up@@ -259,3 +282,30 @@ function cookieRef<T> (value: T | undefined, delay: number, shouldWatch: boolean
}
})
}

/**
* Custom ref that tracks explicit cookie writes on the server.
*
* This is required for the `refresh` option to ensure the cookie is
* re-written on SSR even when the value remains unchanged.
*/
function cookieServerRef<T> (name: string, value: T | undefined) {
const internalRef = ref(value)
const nuxtApp = useNuxtApp()

return customRef((track, trigger) => {
return {
get () {
track()
return internalRef.value
},
set (newValue) {
nuxtApp._cookiesChanged ||= {}
nuxtApp._cookiesChanged[name] = true

internalRef.value = newValue
trigger()
},
}
})
}
1 change: 1 addition & 0 deletionspackages/nuxt/src/app/nuxt.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -111,6 +111,7 @@ interface _NuxtApp {

/** @internal */
_cookies?: Record<string, unknown>
_cookiesChanged?: Record<string, boolean>
/**
* The id of the Nuxt application.
* @internal */
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp