8. Ejemplo [nuxt-05]: persistencia del almacén con una cookie de sesión
Objetivo: queremos que el almacén [Vuex] no se reinicie con cada solicitud al servidor. Para ello, utilizaremos una cookie de sesión:
- el almacén será inicializado por el servidor y este lo colocará en una cookie de sesión;
- el navegador del cliente recibirá esta cookie de sesión y la enviará automáticamente con cada nueva solicitud al servidor;
- el servidor podrá entonces recuperar esta cookie de sesión y trabajar con el almacén que contiene, un almacén actualizado por el cliente;
8.1. Présentation
El proyecto [nuxt-05] se obtiene inicialmente al copiar el proyecto [nuxt-04]:

Veremos que solo cambiará el archivo [store / index.js].
Para utilizar cookies con [nuxt], utilizaremos el módulo [cookie-universal-nuxt], que instalaremos junto con [yarn] en un terminal VSCode:

- en [4], escribimos el comando [yarn add cookie-universal-nuxt];
De esta manera, se agrega un nuevo módulo al archivo [package.json] del proyecto [dvp]:
...
},
"dependencies": {
"@nuxtjs/axios": "^5.3.6",
"bootstrap": "^4.1.3",
"bootstrap-vue": "^2.0.0",
"cookie-universal-nuxt": "^2.0.19",
"nuxt": "^2.0.0"
},
8.2. El archivo de configuración [nuxt.config.js]
Para que [nuxt] pueda utilizar las cookies de [cookie-universal-nuxt], es necesario declarar este módulo en el archivo de configuración [nuxt.config.js]:
...
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
// https://www.npmjs.com/package/cookie-universal-nuxt
'cookie-universal-nuxt'
],
...
- en la línea 12, se agrega el módulo [cookie-universal-nuxt] a la tabla de módulos [6] de [nuxt];
El archivo [nuxt.config.js] queda finalmente así:
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'Introduction à [nuxt.js]',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: 'ssr routing loading asyncdata middleware plugins store'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [],
/*
** Plugins to load before mounting the App
*/
plugins: [],
/*
** Nuxt.js dev-modules
*/
buildModules: [
// Doc: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
// https://www.npmjs.com/package/cookie-universal-nuxt
'cookie-universal-nuxt'
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
},
// directorio del código fuente
srcDir: 'nuxt-05',
// enrutador
router: {
// raíz de los URL de la aplicación
base: '/nuxt-05/'
},
// servidor
server: {
// puerto de servicio, 3000 por defecto
port: 81,
// direcciones de red en las que escucha, por defecto localhost: 127.0.0.1
// 0.0.0.0 = todas las direcciones de red de la máquina
host: 'localhost'
},
// entorno
env: {
maxAge: 60 * 5
}
}
- línea 79: se ha agregado la clave [env] al archivo. Esta clave es una palabra reservada. Los elementos declarados en este objeto están disponibles a partir del objeto [context.env] en los elementos de la aplicación;
- línea 80: el atributo [maxAge] representará la vida útil máxima de la cookie de sesión, la cual se mide a partir de la última vez que se inicializó la cookie. Esta duración se expresa en segundos. Aquí se ha establecido una vida útil de 5 minutos;
8.3. El principio de la persistencia del almacén
Las cookies intercambiadas entre el cliente y el servidor están disponibles en ambos lados (cliente y servidor) en:
- [context.app.$cookies], donde el objeto [context] está disponible, es decir, prácticamente en todas partes;
- [this.$cookies] dentro de una vista;
Se obtiene una cookie específica con la expresión [...$cookies.get(‘nom_du_cookie’)]. Se establece el valor de una cookie con la expresión [...$cookies.set(‘nom_du_cookie’, valeur_du_cookie)].
El principio de la cookie de persistencia del almacén será el siguiente:
- cuando el servidor inicialice el almacén en la función [nuxtServerInit], el estado del almacén se guardará en una cookie llamada «session»;
- la cookie «session» formará entonces parte de la respuesta HTTP del servidor. Sabemos que un navegador devuelve al servidor las cookies que este le ha enviado. Lo hace con cada nueva solicitud que realiza al servidor. También sabemos que el servidor envía el almacén dentro de la página que envía al cliente;
- dentro del navegador, la aplicación cliente recupera el «store» enviado por el servidor y luego realiza su trabajo. Nos aseguraremos de que, cada vez que modifique el «store», su nuevo estado se almacene en la cookie «session» registrada por el navegador;
- si el usuario fuerza una llamada al servidor, el navegador del cliente reenviará automáticamente todas las cookies que el servidor le haya enviado previamente, en particular la cookie llamada «session»;
- cuando, tras esta llamada, el servidor reinicie nuevamente el almacén, recuperará la cookie llamada «session» e inicializará el estado del almacén con el valor de esta;
- por lo tanto, habrá continuidad del almacén entre el cliente y el servidor;
8.4. Inicialización del almacén
El almacén está implementado en el archivo [store / index.js]:
/* eslint-disable no-console */
export const state = () => ({
// contador
counter: 0
})
export const mutations = {
// incremento del contador en un valor [inc]
increment(state, inc) {
state.counter += inc
},
// sustitución del estado
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
}
}
export const actions = {
async nuxtServerInit(store, context) {
// ¿Quién ejecuta este código?
console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
// se espera el final de una promesa
await new Promise(function(resolve, reject) {
// Normalmente aquí hay una función asíncrona
// la simulamos con una espera de un segundo
setTimeout(() => {
// inicializar sesión
initStore(store, context)
// éxito
resolve()
}, 1000)
})
}
}
function initStore(store, context) {
// ¿Hay una cookie de sesión en la solicitud actual?
const cookies = context.app.$cookies
const session = cookies.get('session')
if (!session) {
// no hay sesión existente
console.log("nuxtServerInit, initialisation d'une nouvelle session")
// se inicializa el almacén
store.commit('increment', 77)
} else {
console.log("nuxtServerInit, reprise d'une session existante")
// se actualiza el almacén con la cookie de sesión
store.commit('replace', session.store)
}
// Se coloca el almacén en la cookie de sesión
cookies.set('session', { store: store.state }, { path: context.base, maxAge: context.env.maxAge })
// registro
console.log('initStore terminé, store=', store.state)
}
Comentarios
- líneas 2-5: el almacén estará compuesto por un contador;
- líneas 9-11: este contador podrá incrementarse;
- líneas 13-17: el estado del almacén se podrá inicializar a partir de un nuevo estado. Esta función sirve para mostrar una posible inicialización del almacén cuando este no se limita únicamente al contador, como en este caso;
- líneas 21-35: la función [nuxtServerInit] no ha cambiado;
- línea 30: cuando transcurre el tiempo de espera de un segundo, se inicializa el almacén mediante la función de las líneas 38-56;
- líneas 40-41: primero se recupera la cookie llamada «session»:
- durante la primera ejecución de la aplicación y en la primera solicitud enviada al servidor, esta cookie aún no existirá. Entonces se creará (línea 53) y se enviará al navegador del cliente;
- durante la misma ejecución de la aplicación y en las solicitudes n.º 2, 3, ... realizadas al servidor, esta cookie ya existirá, ya que el navegador del cliente la reenviará con cada nueva solicitud realizada al servidor;
- durante una segunda ejecución de la aplicación y en la primera solicitud enviada al servidor, esta cookie también puede existir. De hecho, al finalizar el paso 1, la cookie se almacenó en el navegador con una determinada vida útil. Si no se ha superado dicha vida útil, la cookie denominada «session» se enviará con la primera solicitud enviada al servidor
En resumen, para cada solicitud enviada al servidor: si la cookie «session» ya está almacenada en el navegador del cliente, el servidor la recibirá; de lo contrario, no la recibirá.
- líneas 42-47: si el servidor no recibe la cookie de sesión, entonces el almacén se inicializa en la línea 46;
- luego, en la línea 53, se creará una cookie llamada «session» y se incluirá en la respuesta HTTP del servidor. El valor de la cookie es el objeto [{ store: store.state }]. Por lo tanto, es el estado del almacén y no el almacén en sí lo que se incluye en la cookie de sesión;
- el tercer parámetro de la función [set] es un objeto de opciones:
- [path] indica a qué URL se debe devolver esta cookie. [context.base] es el URL base de la aplicación [nuxt-05]. Esta se define en el archivo [nuxt.config.js]:
// enrutador
router: {
// raíz de los URL de la aplicación
base: '/nuxt-05/'
},
- [maxAge] es la vida útil, en segundos, de la cookie en el navegador. Una vez transcurrido ese tiempo, el navegador ya no la envía al servidor. [context.env.maxAge], a su vez, devuelve un valor registrado en el archivo [nuxt.config.js]:
[env] es una palabra clave reservada del archivo de configuración. Aquí se establece la vida útil en 5 minutos. Este tiempo se mide a partir de la última vez que el navegador recibió la cookie de sesión. Una vez transcurrido este tiempo, la cookie no se reenviará al servidor, el cual deberá iniciar una nueva sesión;
- líneas 48-50: si el servidor recibe la cookie de sesión, entonces el estado del almacén se inicializa con el objeto [store] de la cookie de sesión. Recordemos que este objeto contiene el estado guardado del almacén;
- luego, en la línea 53, la cookie de sesión se incluirá en la respuesta enviada al navegador del cliente:
- la función [get] busca la cookie de sesión en la solicitud recibida por el servidor;
- la función [set] incluye la cookie de sesión en la respuesta que el servidor envía al navegador del cliente;
- luego, en la línea 53, la cookie de sesión se incluirá en la respuesta enviada al navegador del cliente:
8.5. Incremento del contador de la tienda
El incremento del contador en la página [index.vue] se realiza de la siguiente manera:
// gestión de eventos
methods: {
incrementCounter() {
console.log('incrementCounter')
// incremento del contador en 1
this.$store.commit('increment', 1)
// cambio del valor mostrado
this.value = this.$store.state.counter
// guardar el estado en la cookie de sesión
this.$cookies.set('session', { store: this.$store.state }, { path: this.$nuxt.context.base, maxAge: this.$nuxt.context.env.maxAge })
}
}
Del lado del cliente, cada vez que se modifica el almacén, hay que guardarlo en la cookie de sesión. De hecho, el usuario puede solicitar una URL manualmente en cualquier momento y, por lo tanto, debemos poder enviar al servidor un almacén actualizado. Por eso, en la línea 10, después de incrementar el contador del almacén, se guarda su estado en la cookie de sesión:
- las cookies están disponibles en la propiedad [this.$cookies];
- el estado del almacén [this.$store.state] se guarda en la cookie asociada a la clave [store];
- la ruta de la cookie es [context.base]. En una vista, el contexto está disponible en [this.$nuxt.context];
- la vida útil de la cookie es [context.env.maxAge], disponible aquí en la propiedad [this.$nuxt.context.env.maxAge];
8.6. Ejecución del ejemplo [nuxt-05]
Iniciamos la aplicación [nuxt-05]:

Las siguientes capturas de pantalla corresponden a un navegador Chrome. Solicitamos el URL [http://localhost:81/nuxt-05/]. No olvides el último / detrás de /nuxt-05, de lo contrario no obtendrás los resultados esperados:

- en [4], obtuvimos el valor inicial del store (77);
Revisemos los registros del navegador (F12):

- en [5-6], los registros del servidor;
- en [7], vemos que el servidor inicia una nueva sesión. Esto significa que no recibió una cookie de sesión;
- en [8], se inicializa el contador con el valor 77;
- en [9], la página [index] del servidor (9) y la del cliente (10) muestran efectivamente el mismo valor del contador;
Ahora veamos las cookies recibidas por el navegador:

- En [1], selecciona la pestaña [Application] y luego la opción [Cookies] [2]. Entre todas las cookies de su navegador, elija la del dominio [http://localhost:81];
- en [4], la cookie llamada «session». Si no la tiene, actualice la página [F5]: tal vez haya excedido su tiempo de vida, que es de 5 minutos;
- en [5], el valor de la cookie. Aunque no es muy legible debido a la codificación de los caracteres { :, se distingue el valor 77 del contador;
- en [6], el URL de la cookie: cada vez que se solicite este URL, el navegador enviará la cookie al servidor;
- en [7], la hora de vencimiento de la cookie. Cuando se supere esta hora, la cookie se eliminará del navegador;
Asegúrate de tener esta cookie. Si no la tienes, actualiza la página (F5). Cuando tengas la página con su cookie, actualiza la página nuevamente (F5). Los registros quedarán entonces así:

Esta vez, en [3], el servidor sí recuperó la cookie de sesión. Fue el navegador del cliente quien se la envió.
Ahora, ve incrementando el contador y, de vez en cuando, actualiza la página actual (F5), ya sea [index] o [page1], deberás observar que el contador no vuelve a 77 como en el ejemplo [nuxt-04], sino que conserva el valor que tenía en el navegador del cliente antes de recargar la página:


Los registros del navegador son entonces los siguientes:

Nota: para las pruebas, es posible que necesites eliminar [5], la cookie de sesión almacenada en el navegador, para comenzar con una nueva sesión, inicializada por el servidor, en la próxima solicitud al mismo.
Por último, veamos cómo la función [incrementCounter] de la página [index] influye en la cookie de sesión almacenada en el navegador del cliente:
// gestión de eventos
methods: {
incrementCounter() {
console.log('incrementCounter')
// incremento del contador en 1
this.$store.commit('increment', 1)
// cambio del valor mostrado
this.value = this.$store.state.counter
// almacenamiento del valor en la cookie de sesión
this.$cookies.set('session', { store: this.$store.state }, { path: this.$nuxt.context.base, maxAge: this.$nuxt.context.env.maxAge })
}
}
- línea 10: el cambio en el contador se refleja en la cookie de sesión;
Verifiquemos este punto. Partimos de la siguiente situación:

- en [4], el contador de la cookie de sesión refleja correctamente el valor mostrado [1];
Ahora, incrementemos el contador una vez: [5]. La cookie de sesión, que era [4], cambia de la siguiente manera:

- a [7]; el contador de la cookie de sesión ha pasado efectivamente a 84. Para verlo, hay que actualizar la vista [8]. Para ello, seleccione otra opción de [Storage] (por ejemplo, [9]) y, a continuación, vuelva a seleccionar la opción [8]. El nuevo valor de la cookie de sesión debería aparecer entonces;