Creación de aplicaciones Vue.js renderizadas del lado del servidor mediante Nuxt.js

Publicado: 2022-03-11

Los marcos/bibliotecas de JavaScript como Vue pueden ofrecer una experiencia de usuario fantástica al navegar por su sitio. La mayoría ofrece una forma de cambiar dinámicamente el contenido de la página sin tener que enviar una solicitud al servidor cada vez.

Sin embargo, hay un problema con este enfoque. Cuando carga inicialmente su sitio web, su navegador no recibe una página completa para mostrar. En su lugar, se envían un montón de piezas para construir la página (HTML, CSS, otros archivos) e instrucciones sobre cómo juntarlos todos (un marco/biblioteca de JavaScript) Se necesita una cantidad de tiempo mensurable para juntar toda esta información antes de que su navegador realmente tenga algo que mostrar. Es como recibir un montón de libros junto con una librería de paquete plano. Primero tendrías que construir la librería y luego llenarla con los libros.

La solución a esto es inteligente: tenga una versión del marco/biblioteca en el servidor que pueda crear una página lista para mostrar. Luego, envíe esta página completa al navegador junto con la capacidad de realizar más cambios y aún tenga contenido de página dinámico (el marco/biblioteca), como si se le enviara una estantería lista para usar junto con algunos libros. Claro, todavía tienes que poner los libros en la librería, pero tienes algo utilizable de inmediato.

Comparación visual del renderizado del lado del cliente y del lado del servidor

Más allá de la analogía tonta, también hay un montón de otras ventajas. Por ejemplo, una página que rara vez cambia, como una página Acerca de nosotros, no necesita volver a crearse cada vez que un usuario la solicita. Entonces, un servidor puede crearlo una vez y luego almacenarlo en caché o almacenarlo en algún lugar para uso futuro. Este tipo de mejoras de velocidad pueden parecer diminutas, pero en un entorno donde el tiempo hasta la capacidad de respuesta se mide en milisegundos (o menos), cada detalle cuenta.

Si desea obtener más información sobre las ventajas de SSR en un entorno Vue, debe consultar el artículo de Vue sobre SSR. Hay una variedad de opciones para lograr estos resultados, pero la más popular, que también recomienda el equipo de Vue, es Nuxt.

Por qué Nuxt.js

Nuxt.js se basa en una implementación de SSR para la popular biblioteca React llamada Next. Después de ver las ventajas de este diseño, se diseñó una implementación similar para Vue llamada Nuxt. Quienes estén familiarizados con la combinación React+Next notarán un montón de similitudes en el diseño y el diseño de la aplicación. Sin embargo, Nuxt ofrece funciones específicas de Vue para crear una solución de SSR potente pero flexible para Vue.

Nuxt se actualizó a una versión 1.0 lista para producción en enero de 2018 y es parte de una comunidad activa y bien respaldada. Una de las mejores cosas es que construir un proyecto usando Nuxt no es tan diferente de construir cualquier otro proyecto Vue. De hecho, proporciona un montón de características que le permiten crear bases de código bien estructuradas en una cantidad de tiempo reducida.

Otra cosa importante a tener en cuenta es que Nuxt no tiene que usarse para SSR . Se promociona como un marco para crear aplicaciones Vue.js universales e incluye un comando ( nuxt generate ) para crear aplicaciones Vue generadas estáticas usando la misma base de código. Entonces, si tiene miedo de sumergirse profundamente en SSR, no se asuste. En su lugar, siempre puede crear un sitio estático mientras aprovecha las funciones de Nuxt.

Para aprovechar el potencial de Nuxt, creemos un proyecto simple. El código fuente final de este proyecto está alojado en GitHub si desea verlo, o puede ver una versión en vivo creada con nuxt generate y alojada en Netlify.

Creando un Proyecto Nuxt

Para comenzar, usemos un generador de proyectos de Vue llamado vue-cli para crear rápidamente un proyecto de muestra:

 # install vue-cli globally npm install -g vue-cli # create a project using a nuxt template vue init nuxt-community/starter-template my-nuxt-project

Después de pasar por un par de opciones, esto creará un proyecto dentro de la carpeta my-nuxt-project o lo que haya especificado. Luego solo necesitamos instalar las dependencias y ejecutar el servidor:

 cd my-nuxt-project npm install # Or yarn npm run dev

Aquí vamos. Abra su navegador en localhost:3000 y su proyecto debería estar ejecutándose. No es muy diferente de crear un proyecto Vue Webpack. Sin embargo, cuando observamos la estructura real de la aplicación, no hay mucho allí, especialmente cuando se compara con algo como la plantilla Vue Webpack.

Diagrama de directorios de proyectos y su relación con el archivo de configuración de Nuxt

Mirar en el package.json también muestra que solo tenemos una dependencia, Nuxt en sí. Esto se debe a que cada versión de Nuxt está diseñada para funcionar con versiones específicas de Vue, Vue-router y Vuex y las agrupa todas para usted.

También hay un archivo nuxt.config.js en la raíz del proyecto. Esto le permite personalizar un montón de funciones que proporciona Nuxt. De forma predeterminada, establece las etiquetas de encabezado, el color de la barra de carga y las reglas de ESLint por usted. Si está ansioso por ver lo que puede configurar, aquí está la documentación; Cubriremos algunas opciones en este artículo.

Entonces, ¿qué tienen de especial esos directorios?

Diseño del proyecto

Si navega a través de los directorios creados, todos ellos tienen un Léame adjunto que indica un breve resumen de lo que pasa en ese directorio y, a menudo, un enlace a los documentos.

Este es uno de los beneficios de usar Nuxt: una estructura predeterminada para su aplicación. Cualquier buen desarrollador front-end estructurará una aplicación similar a esta, pero hay muchas ideas diferentes sobre las estructuras, y cuando se trabaja en equipo, inevitablemente se dedicará algo de tiempo a discutir o elegir esta estructura. Nuxt te proporciona uno.

Nuxt buscará ciertos directorios y construirá su aplicación para usted dependiendo de lo que encuentre. Examinemos estos directorios uno por uno.

Paginas

Este es el único directorio requerido . Cualquier componente de Vue en este directorio se agrega automáticamente a vue-router según sus nombres de archivo y la estructura del directorio. Esto es extremadamente conveniente. Normalmente tendría un directorio de páginas separado de todos modos y tendría que registrar manualmente cada uno de esos componentes en otro archivo de enrutador. Este archivo de enrutador puede volverse complejo para proyectos más grandes y es posible que deba dividirse para mantener la legibilidad. En cambio, Nuxt manejará toda esta lógica por ti.

Para demostrarlo, podemos crear un componente Vue llamado about.vue dentro del directorio Pages. Agreguemos una plantilla simple como:

 <template> <h1>About Page</h1> </template>

Cuando guarde, Nuxt volverá a generar las rutas por usted. Como llamamos a nuestro componente about.vue , si navega a /about , debería ver ese componente. Sencillo.

Hay un nombre de archivo que es especial. Nombrar un archivo index.vue creará una ruta raíz para ese directorio. Cuando se genera el proyecto, ya hay un componente index.vue en el directorio de páginas que se correlaciona con la página de inicio o la página de destino de su sitio. (En el ejemplo de desarrollo, sería simplemente localhost:3000 ).

Nuxt escanea los archivos Vue en el directorio de páginas y genera las páginas apropiadas.

¿Qué pasa con las rutas más profundas? Los subdirectorios en el directorio de páginas ayudan a estructurar sus rutas. Entonces, si quisiéramos una página Ver producto, podríamos estructurar nuestro directorio de páginas de la siguiente manera:

 /pages --| /products ----| index.vue ----| view.vue

Ahora, si navegamos a /products/view , veremos el componente view.vue dentro del directorio de productos. Si navegamos a /products , veremos el componente index.vue dentro del directorio de productos.

Es posible que se pregunte por qué no creamos simplemente un componente products.vue en el directorio de páginas, como lo hicimos para la página /about . Puede pensar que el resultado sería el mismo, pero hay una diferencia entre las dos estructuras. Demostremos esto agregando otra página nueva.

Digamos que queríamos una página Acerca de separada para cada empleado. Por ejemplo, vamos a crear una página Acerca de para mí. Debe estar ubicado en /about/ben-jones . Inicialmente, podemos intentar estructurar el directorio de páginas de esta manera:

 /pages --| about.vue --| /about ----| ben-jones.vue

Cuando intentamos acceder a /about/ben-jones , en su lugar obtenemos el componente about.vue , lo mismo que /about . ¿Que está pasando aqui?

Curiosamente, lo que hace Nuxt aquí es generar una ruta anidada . Esta estructura sugiere que desea una ruta permanente /about y cualquier cosa dentro de esa ruta debe anidarse en su propia área de visualización. En vue-router, esto se indicaría especificando un componente <router-view /> dentro del componente about.vue . En Nuxt, este es el mismo concepto excepto que, en lugar de <router-view /> , simplemente usamos <nuxt /> . Así que actualicemos nuestro componente about.vue para permitir rutas anidadas:

 <template> <div> <h1>About Page</h1> <nuxt /> </div> </template>

Ahora, cuando navegamos a /about , obtenemos el componente about.vue que teníamos antes, con solo un título. Sin embargo, cuando navegamos a /about/ben-jones , tenemos el título y el componente ben-jones.vue donde estaba el marcador de posición <nuxt/> .

Esto no era lo que inicialmente queríamos, pero la idea de tener una página Acerca de con una lista de personas que, al hacer clic en ellas, llenen una sección de la página con su información es un concepto interesante, así que dejémoslo como está por ahora. . Si quisiera la otra opción, todo lo que haríamos sería reestructurar nuestros directorios. Solo tendríamos que mover el componente about.vue dentro del directorio /about y renombrarlo index.vue , por lo que la estructura resultante sería:

 /pages --| /about ----| index.vue ----| ben-jones.vue

Finalmente, digamos que queremos usar parámetros de ruta para recuperar un producto específico. Por ejemplo, queremos poder editar un producto navegando a /products/edit/64 donde 64 es product_id . Podemos hacer esto de la siguiente manera:

 /pages --| /products ----| /edit ------| _product_id.vue

Tenga en cuenta el guión bajo al comienzo del componente _product_id.vue : esto significa un parámetro de ruta al que luego se puede acceder en el objeto $route.params o en el objeto params en el Contexto de Nuxt (más sobre eso más adelante). Tenga en cuenta que la clave para el parámetro será el nombre del componente sin el guión bajo inicial, en este caso, product_id , así que trate de mantenerlos únicos dentro del proyecto. Como resultado, en _product_id.vue , podemos tener algo como:

 <template> <h1>Editing Product {{ $route.params.product_id }}</h1> </template>

Puede comenzar a imaginar diseños más complejos, lo que sería difícil de configurar con vue-router. Por ejemplo, podemos combinar todo lo anterior en una ruta como:

 /pages --| /categories ----| /_category_id ------| products.vue ------| /products --------| _product_id.vue

No es demasiado difícil razonar sobre lo que mostraría /categories/2/products/3 . Tendríamos el componente products.vue con un componente _product_id.vue anidado , con dos parámetros de ruta: category_id y product_id . Esto es mucho más simple de razonar que una configuración de enrutador equivalente.

Mientras estamos en el tema, una cosa que tiendo a hacer en la configuración del enrutador es configurar los protectores del enrutador. Como Nuxt está construyendo el enrutador para nosotros, esto se puede hacer en el componente mismo con beforeRouteEnter . Si desea validar los parámetros de la ruta, Nuxt proporciona un método de componente llamado validate . Entonces, si quisiera verificar si product_id era un número antes de intentar representar el componente, agregaría lo siguiente a la etiqueta de script de _product_id.vue :

 export default { validate ({ params }) { // Must be a number return /^\d+$/.test(params.product_id) } }

Ahora, navegar a /categories/2/products/someproduct da como resultado un 404 porque someproduct no es un número válido.

Eso es todo para el directorio de páginas. Es esencial aprender a estructurar correctamente sus rutas en este directorio, por lo que dedicar un poco de tiempo al principio es importante para aprovechar Nuxt al máximo. Si está buscando una breve descripción general, siempre es útil consultar los documentos para el enrutamiento.

Si le preocupa no tener el control del enrutador, no lo haga. Esta configuración predeterminada funciona muy bien para una amplia variedad de proyectos, siempre que estén bien estructurados. Sin embargo, hay algunos casos en los que es posible que deba agregar más rutas al enrutador de las que Nuxt genera automáticamente para usted o reestructurarlas. Nuxt proporciona una forma de personalizar la instancia del enrutador en la configuración, lo que le permite agregar nuevas rutas y personalizar las rutas generadas. También puede editar la funcionalidad principal de la instancia del enrutador, incluidas las opciones adicionales agregadas por Nuxt. Entonces, si se encuentra con un caso extremo, aún tiene la flexibilidad para encontrar la solución adecuada.

Tienda

Nuxt puede construir su tienda Vuex basándose en la estructura del directorio de la tienda, similar al directorio de Pages. Si no necesita una tienda, simplemente elimine el directorio. Hay dos modos para la tienda, Clásico y Módulos.

Classic requiere que tenga un archivo index.js en el directorio de la tienda. Allí debe exportar una función que devuelva una instancia de Vuex:

 import Vuex from 'vuex' const createStore = () => { return new Vuex.Store({ state: ..., mutations: ..., actions: ... }) } export default createStore

Esto le permite crear la tienda como desee, como usar Vuex en un proyecto normal de Vue.

El modo de módulos también requiere que cree un archivo index.js en el directorio de la tienda. Sin embargo, este archivo solo necesita exportar el estado raíz/mutaciones/acciones para su tienda Vuex. El siguiente ejemplo especifica un estado raíz en blanco:

 export const state = () => ({})

Luego, cada archivo en el directorio de la tienda se agregará a la tienda en su propio espacio de nombres o módulo. Por ejemplo, creemos un lugar para almacenar el producto actual. Si creamos un archivo llamado product.js en el directorio de la tienda, entonces una sección de espacio de nombres de la tienda estará disponible en $store.product . Aquí hay un ejemplo simple de cómo se ve ese archivo:

 export const state = () => ({ _id: 0, title: 'Unknown', price: 0 }) export const actions = { load ({ commit }) { setTimeout( commit, 1000, 'update', { _id: 1, title: 'Product', price: 99.99 } ) } } export const mutations = { update (state, product) { Object.assign(state, product) } }

setTimeout en la acción de carga simula algún tipo de llamada API, que actualizará la tienda con la respuesta; en este caso, tarda un segundo. Ahora, usémoslo en la página de products/view :

 <template> <div> <h1>View Product {{ product._id }}</h1> <p>{{ product.title }}</p> <p>Price: {{ product.price }}</p> </div> </template> <script> import { mapState } from 'vuex' export default { created () { this.$store.dispatch('product/load') }, computed: { ...mapState(['product']) } } </script>

Algunas cosas a tener en cuenta: Aquí, estamos llamando a nuestra API falsa cuando se crea el componente. Puede ver que la acción de product/load que estamos despachando tiene un espacio de nombres en Producto. Esto deja claro exactamente con qué sección de la tienda estamos tratando. Luego, al asignar el estado a una propiedad computada local, podemos usarlo fácilmente en nuestra plantilla.

Hay un problema: vemos el estado original por un segundo mientras se ejecuta la API. Más tarde, usaremos una solución provista por Nuxt para arreglar esto (conocida como fetch ).

Solo para enfatizar esto nuevamente, nunca tuvimos que npm install vuex por npm, ya que ya está incluido en el paquete Nuxt. Cuando agrega un archivo index.js al directorio de la tienda, todos esos métodos se abren para usted automáticamente .

Esos son los dos directorios principales explicados; el resto son mucho más simples.

Componentes

El directorio de componentes está ahí para contener sus componentes reutilizables, como una barra de navegación, galería de imágenes, paginación, tablas de datos, etc. Dado que los componentes en el directorio de páginas se convierten en rutas, necesita otro lugar para almacenar este tipo de componentes. Se puede acceder a estos componentes en páginas u otros componentes al importarlos:

 import ComponentName from ~/components/ComponentName.vue

Activos

Esto contiene activos no compilados y tiene más que ver con cómo Webpack carga y procesa los archivos, que con cómo funciona Nuxt. Si está interesado, le sugiero que lea la guía en el archivo Léame.

Estático

Este contiene archivos estáticos que están asignados al directorio raíz de su sitio. Por ejemplo, si coloca una imagen llamada logo.png en este directorio, estará disponible en /logo.png . Esto es bueno para metaarchivos como robots.txt, favicon.ico y otros archivos que necesita disponibles.

Diseños

Normalmente, en un proyecto de Vue, tiene algún tipo de componente raíz, normalmente llamado App.vue . Aquí es donde puede configurar el diseño de su aplicación (normalmente estático), que puede incluir una barra de navegación, un pie de página y luego un área de contenido para su vue-router. El diseño default hace exactamente eso y se proporciona en la carpeta de diseños. Inicialmente, todo lo que tiene es un div con un <nuxt /> (que es equivalente a <router-view /> ) pero se le puede aplicar el estilo que desee. Por ejemplo, agregué una barra de navegación simple al proyecto de ejemplo para navegar por las distintas páginas de demostración.

Un diseño se puede aplicar a varias páginas.

Es posible que desee tener un diseño diferente para una determinada sección de su aplicación. Tal vez tenga algún tipo de CMS o panel de administración que se vea diferente. Para resolver esto, cree un nuevo diseño en el directorio Diseños. Como ejemplo, creemos un diseño admin-layout.vue que solo tiene una etiqueta de encabezado adicional y no tiene barra de navegación:

 <template> <div> <h1>Admin Layout</h1> <nuxt /> </div> </template>

Luego, podemos crear una página admin.vue en el directorio de Páginas y usar una propiedad proporcionada por Nuxt llamada layout para especificar el nombre (como una cadena) del diseño que queremos usar para ese componente:

 <template> <h1>Admin Page</h1> </template> <script> export default { layout: 'admin-layout' } </script>

Eso es todo al respecto. Los componentes de la página usarán el diseño default a menos que se especifique, pero cuando navega a /admin , ahora usa el diseño admin-layout.vue . Por supuesto, este diseño podría compartirse en varias pantallas de administración si lo desea. Lo único importante que debe recordar es que los diseños deben contener un <nuxt /> .

Hay una última cosa a tener en cuenta sobre los diseños. Es posible que haya notado mientras experimentaba que si escribe una URL no válida, se muestra una página de error. Esta página de error es, de hecho, otro diseño. Nuxt tiene su propio diseño de error (código fuente aquí), pero si desea editarlo, simplemente cree un diseño error.vue y se usará en su lugar. La advertencia aquí es que el diseño de error no debe tener un <nuxt /> . También tendrá acceso a un objeto de error en el componente con información básica para mostrar. (Esto está impreso en la terminal que ejecuta Nuxt si desea examinarlo).

software intermedio

El middleware son funciones que se pueden ejecutar antes de representar una página o un diseño. Hay una variedad de razones por las que puede querer hacerlo. La protección de rutas es un uso popular en el que puede verificar la tienda Vuex para obtener un inicio de sesión válido o validar algunos parámetros (en lugar de usar el método de validate en el componente mismo). Un proyecto en el que trabajé recientemente usó middleware para generar migas de pan dinámicas basadas en la ruta y los parámetros.

Estas funciones pueden ser asíncronas; solo tenga cuidado, ya que no se mostrará nada al usuario hasta que se resuelva el middleware. También tienen acceso al Contexto de Nuxt, que explicaré más adelante.

Complementos

Este directorio le permite registrar los complementos de Vue antes de crear la aplicación. Esto permite que el complemento se comparta en toda su aplicación en la instancia de Vue y sea accesible en cualquier componente.

La mayoría de los complementos principales tienen una versión de Nuxt que se puede registrar fácilmente en la instancia de Vue siguiendo sus documentos. Sin embargo, habrá circunstancias en las que desarrolle un complemento o necesite adaptar un complemento existente para este propósito. Un ejemplo que tomo prestado de los documentos muestra cómo hacer esto para vue-notifications . Primero, necesitamos instalar el paquete:

 npm install vue-notifications --save

Luego cree un archivo en el directorio de complementos llamado vue-notifications.js e incluya lo siguiente:

 import Vue from 'vue' import VueNotifications from 'vue-notifications' Vue.use(VueNotifications)

Muy similar a cómo registraría un complemento en un entorno normal de Vue. Luego edite el archivo nuxt.config.js en la raíz de su proyecto y agregue la siguiente entrada al objeto module.exports:

 plugins: ['~/plugins/vue-notifications']

Eso es todo. Ahora puede usar vue-notifications en toda su aplicación. Un ejemplo de esto está en /plugin en el proyecto de ejemplo.

Eso completa un resumen de la estructura del directorio. Puede parecer mucho para aprender, pero si está desarrollando una aplicación Vue, ya está configurando el mismo tipo de lógica. Nuxt ayuda a abstraer la configuración y lo ayuda a concentrarse en la construcción.

Sin embargo, Nuxt hace más que ayudar en el desarrollo. Supercarga sus componentes al proporcionar funcionalidad adicional.

Componentes sobrealimentados de Nuxt

Cuando comencé a investigar sobre Nuxt, seguí leyendo acerca de cómo se sobrealimentan los componentes de Page. Sonaba genial, pero no era inmediatamente obvio qué significaba exactamente y qué beneficios trae.

Lo que significa es que todos los componentes de la página tienen métodos adicionales adjuntos que Nuxt puede usar para proporcionar funcionalidad adicional. De hecho, ya vimos uno de estos anteriormente cuando usamos el método de validate para verificar los parámetros y redirigir a un usuario si no son válidos.

Los dos principales utilizados en un proyecto de Nuxt serán los métodos asyncData y fetch . Ambos son muy similares en concepto, se ejecutan de forma asíncrona antes de que se genere el componente y se pueden usar para completar los datos de un componente y la tienda. También permiten que la página se represente por completo en el servidor antes de enviarla al cliente, incluso cuando tenemos que esperar alguna llamada de base de datos o API.

¿Cuál es la diferencia entre asyncData y fetch ?

  • asyncData se usa para completar los datos del componente de página. Cuando devuelve un objeto, se fusiona con la salida de data antes de la representación.
  • fetch se usa para llenar la tienda Vuex. Si devuelve una promesa, Nuxt esperará hasta que se resuelva antes de renderizar.

Así que vamos a darles un buen uso. ¿Recuerda que anteriormente en la página /products/view tuvimos un problema en el que el estado inicial de la tienda se mostraba brevemente mientras se realizaba nuestra llamada API falsa? Una forma de solucionar esto es tener un valor booleano almacenado en el componente o en la tienda, como loading = true , y luego mostrar un componente de carga mientras finaliza la llamada a la API. Luego, estableceríamos loading = false y mostraríamos los datos.

En su lugar, usemos fetch para llenar la Tienda antes de renderizar. En una nueva página llamada /products/view-async , cambiemos el método created a fetch ; eso debería funcionar, ¿verdad?

 export default { fetch () { // Unfortunately the below line throws an error // because 'this.$store' is undefined... this.$store.dispatch('product/load') }, computed: {...} }

Aquí está el problema: estos métodos "supercargados" se ejecutan antes de que se cree el componente, por this no apunta al componente y no se puede acceder a nada en él. Entonces, ¿cómo accedemos a la tienda aquí?

La API de contexto

Por supuesto, hay una solución. En todos los métodos de Nuxt, se le proporciona un argumento (normalmente el primero) que contiene un objeto extremadamente útil llamado Contexto. Esto es todo lo que necesitará como referencia en la aplicación, lo que significa que no necesitamos esperar a que Vue cree esas referencias en el componente primero.

Recomiendo encarecidamente consultar los documentos de contexto para ver qué hay disponible. Algunos prácticos son app , donde puede acceder a todos sus complementos, redirect , que se puede usar para cambiar rutas, error para mostrar la página de error y algunos que se explican por sí mismos como route , query y store .

Entonces, para acceder a la Tienda, podemos desestructurar el Contexto y extraer la Tienda de él. También debemos asegurarnos de devolver una promesa para que Nuxt pueda esperar a que se resuelva antes de renderizar el componente, por lo que también debemos hacer un pequeño ajuste en nuestra acción de Tienda.

 // Component export default { fetch ({ store }) { return store.dispatch('product/load') }, computed: {...} } // Store Action load ({ commit }) { return new Promise(resolve => { setTimeout(() => { commit('update', { _id: 1, title: 'Product', price: 99.99 }) resolve() }, 1000) }) }

Podría usar async/await u otros métodos según su estilo de codificación, pero el concepto es el mismo: le estamos diciendo a Nuxt que se asegure de que la llamada a la API finalice y que Store se actualice con el resultado antes de intentar renderizar el componente. Si intenta navegar a /products/view-async , no verá el destello de contenido donde se encuentra el producto en su estado inicial.

Puede imaginar lo útil que puede ser esto en cualquier aplicación Vue, incluso sin SSR. El contexto también está disponible para todos los middlewares , así como para otros métodos de Nuxt, como NuxtServerInit , que es una acción especial de la tienda que se ejecuta antes de que se inicialice la tienda (un ejemplo de esto se encuentra en la siguiente sección).

Consideraciones al usar SSR

Estoy seguro de que muchos (incluido yo mismo) que comienzan a usar una tecnología como Nuxt mientras la tratan como cualquier otro proyecto de Vue eventualmente chocan con una pared donde algo que sabemos que normalmente funcionaría parece imposible en Nuxt. A medida que se documenten más de estas advertencias, será más fácil superarlas, pero lo más importante a tener en cuenta al comenzar la depuración es que el cliente y el servidor son dos entidades separadas.

Cuando accede inicialmente a una página, se envía una solicitud a Nuxt, el servidor crea la mayor cantidad posible de esa página y el resto de la aplicación, y luego el servidor se la envía. Luego, la responsabilidad recae en el cliente para continuar con la navegación y cargar fragmentos según los necesite.

Queremos que el servidor haga todo lo posible primero, pero a veces no tiene acceso a la información que necesita, lo que hace que el trabajo se realice del lado del cliente. O peor aún, cuando el contenido final presentado por el cliente es diferente de lo que esperaba el servidor, se le dice al cliente que lo reconstruya desde cero. Esta es una gran indicación de que algo anda mal con la lógica de la aplicación. Afortunadamente, se generará un error en la consola de su navegador (en modo de desarrollo) si esto comienza a suceder.

Tomemos un ejemplo de cómo resolver un problema común, la gestión de sesiones. Imagine que tiene una aplicación Vue donde puede iniciar sesión en una cuenta y su sesión se almacena mediante un token (JWT, por ejemplo) que decide mantener en localStorage . Cuando accede inicialmente al sitio, desea autenticar ese token con una API, que devuelve información básica del usuario si es válida y coloca esa información en la Tienda.

Después de leer los documentos de Nuxt, verá que hay un método útil llamado NuxtServerInit que le permite llenar de forma asincrónica la tienda una vez en la carga inicial. ¡Eso suena perfecto! Así que crea su módulo de usuario en la Tienda y agrega la acción apropiada en el archivo index.js en el directorio de la Tienda:

 export const actions = { nuxtServerInit ({ dispatch }) { // localStorage should work, right? const token = localStorage.getItem('token') if (token) return dispatch('user/load', token) } }

Cuando actualiza la página, obtiene un error, localStorage is not defined . Pensando en dónde está sucediendo esto, tiene sentido. Este método se ejecuta en el servidor, no tiene idea de lo que se almacena en localStorage en el cliente; de hecho, ¡ni siquiera sabe qué es "localStorage"! Así que esa no es una opción.

El servidor intenta ejecutar localStorage.getItem('token') pero arroja un error y, a continuación, un título que explica el problema.

Entonces, ¿cuál es la solución? Hay algunos, en realidad. En su lugar, puede hacer que el cliente inicialice Store, pero termine perdiendo los beneficios de SSR porque el cliente termina haciendo todo el trabajo. Puede configurar sesiones en el servidor y luego usar eso para autenticar al usuario, pero esa es otra capa para configurar. Lo que es más similar al método localStorage es usar cookies en su lugar.

Nuxt tiene acceso a las cookies porque se envían con la solicitud del cliente al servidor. Al igual que con otros métodos de Nuxt, nuxtServerInit tiene acceso al contexto, esta vez como segundo argumento porque el primero está reservado para la tienda. En Contexto, podemos acceder al objeto req , que almacena todos los encabezados y otra información de la solicitud del cliente. (Esto le resultará especialmente familiar si ha utilizado Node.js).

Entonces, después de almacenar el token en una cookie (llamada "token", en este caso), accedamos a él en el servidor.

 import Cookie from 'cookie' export const actions = { nuxtServerInit ({ dispatch }, { req }) { const cookies = Cookie.parse(req.headers.cookie || '') const token = cookies['token'] || '' if (token) return dispatch('user/load', token) } }

Una solución simple, pero que podría no ser inmediatamente obvia. Aprender a pensar en dónde están ocurriendo ciertas acciones (cliente, servidor o ambos) y a qué tienen acceso lleva algo de tiempo, pero los beneficios valen la pena.

Despliegue

La implementación con Nuxt es extremadamente simple. Con el mismo código base, puede crear una aplicación SSR, una aplicación de una sola página o una página estática.

Aplicación renderizada del lado del servidor (aplicación SSR)

Esto es probablemente lo que buscabas al usar Nuxt. El concepto básico para la implementación aquí es ejecutar el proceso de build en cualquier plataforma que elija y establecer algunas configuraciones. Usaré el ejemplo de Heroku de los documentos:

Primero, configure scripts para Heroku en package.json :

 "scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "heroku-postbuild": "npm run build" }

Luego configure el entorno de Heroku usando heroku-cli (instrucciones de configuración aquí:

 # set Heroku variables heroku config:set NPM_CONFIG_PRODUCTION=false heroku config:set HOST=0.0.0.0 heroku config:set NODE_ENV=production # deploy git push heroku master

Eso es todo. Ahora su aplicación SSR Vue está lista para que el mundo la vea. Otras plataformas tienen configuraciones diferentes, pero el proceso es similar. Los métodos de implementación oficiales enumerados actualmente son:

  • Ahora
  • Dokku (Océano digital)
  • Nginx

Solicitud de una sola página (SPA)

Si desea aprovechar algunas de las funciones adicionales que ofrece Nuxt pero evitar que el servidor intente generar páginas, puede implementarlo como un SPA.

Primero, es mejor probar su aplicación sin SSR ya que, de forma predeterminada npm run dev se ejecuta con SSR activado. Para cambiar eso, edite el archivo nuxt.config.js y agregue la siguiente opción:

 mode: 'spa',

Ahora, cuando ejecute npm run dev , SSR se desactivará y la aplicación se ejecutará como SPA para que la pruebe. Esta configuración también garantiza que ninguna compilación futura incluya SSR.

Si todo se ve bien, la implementación es exactamente la misma que para una aplicación SSR. Solo recuerde que primero debe configurar el mode: 'spa' para que el proceso de compilación sepa que desea un SPA.

Páginas estáticas

Si no desea lidiar con un servidor en absoluto y, en cambio, desea generar páginas para usar con servicios de alojamiento estático como Surge o Netlify, entonces esta es la opción que debe elegir. Solo tenga en cuenta que, sin un servidor, no podrá acceder a los req y res del contexto, por lo que si su código se basa en eso, asegúrese de acomodarlo. Por ejemplo, al generar el proyecto de ejemplo, la función nuxtServerInit arroja un error porque intenta obtener un token de las cookies en los encabezados de solicitud. En este proyecto, no importa, ya que esos datos no se usan en ninguna parte, pero en una aplicación real, debería haber una forma alternativa de acceder a esos datos.

Una vez que se soluciona, la implementación es fácil. Una cosa que probablemente necesitará cambiar primero es agregar una opción para que el nuxt generate también cree un archivo de reserva. Este archivo le indicará al servicio de alojamiento que permita que Nuxt maneje el enrutamiento en lugar del servicio de alojamiento, arrojando un error 404. Para hacerlo, agregue la siguiente línea a nuxt.config.js :

 generate: { fallback: true },

Aquí hay un ejemplo usando Netlify, que no está actualmente en los documentos de Nuxt. Solo tenga en cuenta que si es la primera vez que usa netlify-cli , se le pedirá que se autentique:

 # install netlify-cli globally npm install netlify-cli -g # generate the application (outputs to dist/ folder) npm run generate # deploy netlify deploy dist

¡Es tan simple como eso! Como se mencionó al principio del artículo, hay una versión de este proyecto aquí. También hay documentación de implementación oficial para los siguientes servicios a continuación:

  • Aumento
  • Páginas de GitHub

Aprende más

Nuxt se actualiza rápidamente y esta es solo una pequeña selección de las funciones que ofrece. Espero que este artículo lo aliente a probarlo y ver si puede ayudar a mejorar las capacidades de sus aplicaciones Vue, permitiéndole desarrollar más rápido y aprovechar sus potentes funciones.

Si está buscando más información, no busque más allá de los enlaces oficiales de Nuxt:

  • Documentación
  • Patio de recreo
  • GitHub
  • Preguntas más frecuentes

Looking to up your JavaScript game? Try reading The Comprehensive Guide to JavaScript Design Patterns by fellow Toptaler Marko Mišura.