Aprenda a crear una aplicación Nuxt.
CRUD: crear, leer, actualizar, eliminar
Supongo que ya conoce los fundamentos de Vue JS y / o está un poco familiarizado con el marco. Nuxt JS es un marco robusto, construido sobre Vue JS. Es esencialmente lo mismo que Vue JS. Entonces, ¿por qué, Nuxt?
Para la mayoría de las personas, la decisión de utilizar Nuxt JS suele ser por sus capacidades de SSR.
What is SSR?
SSR es una abreviatura de Server Side Rendering.
Por lo general, para la mayoría de las aplicaciones de página única (SPA), los archivos renderizados se inyectan automáticamente en el DOM después de que se haya cargado la página. Por lo tanto, los robots, los rastreadores de SEO encontrarán una página vacía en la carga de la página. Sin embargo, para SSR, debido a su capacidad para pre-renderizar aplicaciones en el servidor antes de la página, los rastreadores de SEO pueden indexar fácilmente esa página. Además, posiblemente hace que la aplicación sea aún más eficiente que un SPA normal.
Nuxt JS ofrece a los desarrolladores la posibilidad de crear aplicaciones SSR con facilidad. Las aplicaciones regulares de Vue JS SPA también se pueden configurar para usar SSR, pero el proceso es algo engorroso y Nuxt JS proporciona un contenedor para manejar toda esa configuración. Aparte del SSR, Nuxt también proporciona una manera fácil de configurar su proyecto VueJS con más eficiencia.
Aunque Nuxt JS sigue siendo Vue JS, tiene algunas diferencias fundamentales en la estructura de su arquitectura de carpetas.
El objetivo de este artículo es que pueda crear una aplicación con Nuxt; por lo tanto, no vamos a profundizar en la arquitectura de carpetas de Nuxt, sin embargo, explicaré rápidamente algunos de los importantes que podríamos necesitar aquí.
Páginas
La carpeta de páginas es una de las diferencias fundamentales con el Vue SPA normal. Representa la carpeta Vistas en la arquitectura Vue normal, además, en Nuxt, los archivos creados en la carpeta Páginas se aprovisionan automáticamente como una ruta. Es decir, cuando crea un archivo index.vue en la carpeta de páginas, automáticamente se convierte en su ruta raíz, es decir, localhost: 3000 /.
Además, cuando crea cualquier otro nombre de archivo.vue, se convierte en una ruta: la creación de about.vue le permite acceder a localhost: 3000 / about.
También puede crear una carpeta dentro de la carpeta Páginas. Si crea una carpeta llamada 'contacto' y dentro de esa carpeta, tiene email.vue, entonces puede acceder a localhost: 3000 / contact / email. Es así de simple. De esta manera, no necesita crear manualmente un archivo router.js como lo haría normalmente con Vue JS para crear sus rutas.
Componentes
Sigue siendo prácticamente lo mismo que con Vue JS, los componentes creados no se aprovisionan automáticamente como rutas.
Estático
La carpeta estática reemplaza la carpeta pública en las aplicaciones regulares de Vue JS, funciona prácticamente de la misma manera. Los archivos aquí no se compilan; se sirven de la misma forma que se almacenan.
Puede leer todo sobre la arquitectura y la estructura en el Página de documentación de Nuxt JS.
Ahora, construyamos algo interesante ...
Building a book store app
Crearemos una aplicación de librería, donde un usuario puede agregar libros que ha leído a una categoría particular que le guste. Se verá así.
Entonces, tendremos un diseño simple como el anterior, solo 3 columnas que contienen las diferentes secciones de los libros. Libros leídos recientemente, libros favoritos y sí, el mejor de los mejores libros (lo confieso, no sabía cómo llamar a esa sección, 🙂)
Entonces, el objetivo aquí es poder agregar el título, el autor y la descripción de un libro a una tarjeta en cualquiera de las secciones, editar libros ya agregados y eliminar un libro existente. No utilizaremos ninguna base de datos, por lo que todo sucede en el estado.
Primero, instalamos Nuxt:
npm install create-nuxt-app
En segundo lugar, después de instalar Nuxt, ahora puede crear el proyecto con el comando,
create-nuxt-app bookStore
Elijo nombrar mi aplicación 'librería'; puedes nombrar algo más genial ^ _ ^
Luego, repasemos las indicaciones restantes, ingrese una descripción,
Nombre del autor, escriba un nombre o presione Intro para conservar los valores predeterminados
Seleccione un administrador de paquetes, con el que se sienta cómodo, ambos están bien
Seleccione un marco de interfaz de usuario. Para este proyecto, usaré Vuetify, por otra parte, cualquier marco de interfaz de usuario con el que se sienta cómodo funcionará bien.
Seleccione un marco de servidor personalizado; no necesitamos ninguno, no seleccionaré ninguno
Módulos adicionales, seleccione lo que desee o seleccione ambos, no los estaríamos usando para esta aplicación.
El pelaje es importante. Vayamos con ESLint.
Si bien las pruebas son importantes, no lo veremos hoy, por lo que ninguno
Modo de renderizado, sí, SSR.
Nota: Elegir SSR no significa que no obtengamos el beneficio de tener un SPA, la aplicación sigue siendo un SPA pero con SSR. La otra opción significa simplemente SPA y sin SSR.
Presiona enter y sigue adelante,
Y nuestro proyecto está creando,
Después de la creación, ahora podemos ir al directorio y ejecutar
yarn dev
si estás usando npm
como su administrador de paquetes, use,
npm run dev
De forma predeterminada, la aplicación se ejecuta en localhost: 3000. Visite el enlace en su navegador y debería ver una página Nuxt predeterminada.
Ahora comencemos con la creación de los componentes que necesitamos. Tendremos tarjetas que muestran la información de cada libro y tendremos un modal que contiene un formulario para ingresar la información del nuevo libro o editar la existente.
Para crear un componente, simplemente cree un nuevo archivo en la carpeta de componentes. Aquí está el código del componente de mi tarjeta.
// BookCard.vue
<template>
<v-card class="mx-auto" max-width="400">
<v-img src="https://cdn.vuetifyjs.com/images/cards/sunshine.jpg" height="200px"></v-img>
<v-card-title>{{bookTitle}}</v-card-title>
<v-card-subtitle>{{bookAuthor}}</v-card-subtitle>
<v-card-text>{{bookDescription}}</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<slot name="button"></slot>
</v-card-actions>
</v-card>
</template>
<script>
export default {
props: ["bookTitle", "bookAuthor", "bookDescription"]
};
</script>
Una explicación rápida de lo que se hizo anteriormente. La imagen está codificada; no nos preocuparemos por eso por ahora. El título del libro, el autor del libro y la descripción del libro se transmiten a este componente desde la página principal como accesorios. Si no está familiarizado con los accesorios, imagínelos como puntos de entrada a través de este componente que se pueden completar con datos.
Ahora al siguiente componente, el modal.
//BookModal.vue
<template>
<v-dialog max-width="500px" v-model="open">
<v-card class="p-5">
<v-card-title>Add Books</v-card-title>
<v-form>
<v-select v-model="category" :items="categories" label="Select A Category"></v-select>
<v-text-field v-model="title" label="Enter Book Title"></v-text-field>
<v-text-field v-model="author" label="Enter Book Author"></v-text-field>
<v-textarea v-model="description" label="Enter Book Description"></v-textarea>
</v-form>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn @click.stop="saveBook" color="green">Add</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
Ahora, ese es el marcado para el modal; necesitamos crear los modelos v como propiedades de datos; por lo tanto, agregaremos una etiqueta de secuencia de comandos debajo del etiqueta.
<script>
export default {
data() {
return {
category: "",
title: "",
author: "",
description: "",
};
},
}
</script>
Además, hay un menú desplegable 'Seleccionar una categoría' que espera datos de 'categorías'. Agregaremos eso a los datos.
<script>
export default {
data() {
return {
open: false,
category: "",
title: "",
author: "",
description: "",
categories: ["Recently read books", "Favourite books", "Best of the best"]
};
},
}
</script>
Ahora, necesitamos una forma de alternar nuestra apertura y cierre modal, por ahora, solo tendremos una propiedad de datos 'abierta' como la anterior. Veremos eso de cerca a continuación.
Creemos rápidamente nuestra página de vista donde tendremos tres cuadrículas / columnas, una para cada sección del libro. Llamemos a la página index.vue, vea el código a continuación.
//index.vue
<template>
<div>
<v-row>
<v-col md="4">
<h2 class="text-center mb-5">Recently Read Books</h2>
</v-col>
<v-col md="4">
<h2 class="text-center mb-5">Favourite Books</h2>
</v-col>
<v-col md="4">
<h2 class="text-center mb-5">Best of the Best</h2>
</v-col>
</v-row>
<BookModal />
</div>
</template>
Ahora que tenemos nuestras cuadrículas, debemos agregar nuestro componente de tarjeta a cada cuadrícula, para cada libro agregado. Por lo tanto, importaremos nuestro componente BookCard.vue.
<template>
<div>
<v-row>
<v-col md="4">
<h2 class="text-center mb-5">Recently Read Books</h2>
<v-row v-for="(item,index) in recentBooks" :key="index">
<BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<template v-slot:button>
<v-btn @click.stop="edit(item,index)">Edit</v-btn>
<v-btn @click.stop="remove(item.category, index)">Remove</v-btn>
</template>
</BookCard>
</v-row>
</v-col>
<v-col md="4">
<h2 class="text-center mb-5">Favourite Books</h2>
<v-row v-for="(item,index) in favouriteBooks" :key="index">
<BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<template v-slot:button>
<v-btn @click.stop="edit(item,index)">Edit</v-btn>
<v-btn @click.stop="remove(item.category, index)">Remove</v-btn>
</template>
</BookCard>
</v-row>
</v-col>
<v-col md="4">
<h2 class="text-center mb-5">Best of the Best</h2>
<v-row v-for="(item,index) in bestOfTheBest" :key="index">
<BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<template v-slot:button>
<v-btn @click.stop="edit(item,index)">Edit</v-btn>
<v-btn @click.stop="remove(item.category, index)">Remove</v-btn>
</template>
</BookCard>
</v-row>
</v-col>
</v-row>
</div>
</template>
Ahora, hemos importado el componente BookCard y hemos vinculado sus accesorios a los resultados del ciclo; esto asegura que por cada entrada agregada a cualquiera de las secciones, haya una tarjeta creada para ella. Además, en cada tarjeta, incluiremos botones para editar o eliminar una tarjeta.
Ahora, necesitamos importar la tarjeta desde el script y definir las matrices que contendrán registros para cada una de las categorías.
<script>
import BookCard from "@/components/BookCard";
export default {
components: {
BookCard,
},
data() {
return {
recentBooks: [],
favouriteBooks: [],
bestOfTheBest: []
};
},
};
</script>
A continuación, necesitamos tener un botón en el encabezado que abrirá el modal cada vez que necesitemos agregar libros. Haremos esto en el archivo 'default.vue'. Agregaremos el botón al encabezado predeterminado de la barra de aplicaciones.
<v-btn color="green" @click.stop="openModal">Add Books</v-btn>
A continuación, necesitamos crear el método openModal en la sección de script. En las aplicaciones Vue JS normales, hay un bus de eventos que le permite comunicarse con otro componente e incluso pasar datos, en Nuxt JS, todavía hay un bus de eventos y aún puede crearlo de la misma manera. Entonces, usaremos un bus de eventos para pasar datos para abrir un modal en la página index.vue (que aún no hemos importado) desde el archivo layout / default.vue.
Veamos como se hace.
Para crear un bus de eventos global, abra un archivo en el directorio raíz del proyecto, asígnele el nombre eventBus.js y pegue el código a continuación.
import Vue from 'vue'
export const eventBus = new Vue()
Sí, eso es todo. Ahora podemos usarlo.
<script>
import { eventBus } from "@/eventBus";
methods: {
openModal() {
eventBus.$emit("open-add-book-modal");
}
}
</script>
A continuación, volveremos a nuestro componente BookModal y escucharemos cuando eventBus emite 'open-add-book-modal'. Agregaremos esto a la sección del script.
import { eventBus } from "@/eventBus";
created() {
eventBus.$on("open-add-book-modal", this.open = true);
},
Ahora, podemos abrir y cerrar nuestro modal, pero todavía no agrega ningún libro. Agreguemos un método a nuestro Modal para que guarde lo que se agrega al estado (recuerde que no estamos haciendo uso de ninguna base de datos o almacenamiento local). Agregamos esto junto a 'creado ()'
methods: {
saveBook() {
let cardData = {
title: this.title,
author: this.author,
description: this.description,
category: this.category
};
eventBus.$emit("save-book", cardData);
this.open = false;
}
}
Luego, necesitamos una forma de volver a completar el modal cuando estamos editando datos de cualquiera de las tarjetas. Así que hagamos algunos ajustes en 'created ()'
created() {
eventBus.$on("open-add-book-modal", data => {
if (data) {
this.category = data.category;
this.title = data.title;
this.author = data.author;
this.description = data.description;
}
this.open = true;
});
},
Ahora, el BookModal se ve así como un todo,
//BookModal.vue
<template>
<v-dialog max-width="500px" v-model="open">
<v-card class="p-5">
<v-card-title>Add Books</v-card-title>
<v-form>
<v-select v-model="category" :items="categories" label="Select A Category"></v-select>
<v-text-field v-model="title" label="Enter Book Title"></v-text-field>
<v-text-field v-model="author" label="Enter Book Author"></v-text-field>
<v-textarea v-model="description" label="Enter Book Description"></v-textarea>
</v-form>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn @click.stop="saveBook" color="green">Add</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import { eventBus } from "@/eventBus";
export default {
data() {
return {
open: false,
category: "",
title: "",
author: "",
description: "",
categories: ["Recently read books", "Favourite books", "Best of the best"]
};
},
created() {
eventBus.$on("open-add-book-modal", data => {
if (data) {
this.category = data.category;
this.title = data.title;
this.author = data.author;
this.description = data.description;
}
this.open = true;
});
},
methods: {
saveBook() {
let cardData = {
title: this.title,
author: this.author,
description: this.description,
category: this.category
};
eventBus.$emit("save-book", cardData);
this.open = false;
}
}
};
</script>
A continuación, ahora podemos volver a la página index.vue para importar el componente BookModal. Agregaremos esto a la sección del script.
<script>
import BookCard from "@/components/BookCard";
import BookModal from "@/components/BookModal";
import { eventBus } from "@/eventBus";
export default {
components: {
BookCard,
BookModal
},
data() {
return {
recentBooks: [],
favouriteBooks: [],
bestOfTheBest: []
};
},
</script>
Además, en el cuerpo, agregaremos,
<BookModal/>
Necesitamos métodos para editar y eliminar una tarjeta. En la plantilla anterior, ya pasé los métodos de edición y eliminación a los botones como se muestra a continuación, del mismo modo, pasé los argumentos requeridos para cada método.
<template v-slot:button> <v-btn @click.stop="edit(item,index)">Edit</v-btn> <v-btn @click.stop="remove(item.category, index)">Remove</v-btn> </template>
Creemos los métodos.
methods: {
remove(category, index) {
if (category === "Recently read books") {
this.recentBooks.splice(index, 1);
}
if (category === "Favourite books") {
this.favouriteBooks.splice(index, 1);
}
if (category === "Best of the best") {
this.bestOfTheBest.splice(index, 1);
}
},
edit(item, index) {
if (item.category === "Recently read books") {
eventBus.$emit("open-add-book-modal", item);
this.recentBooks.splice(index, 1);
}
if (item.category === "Favourite books") {
eventBus.$emit("open-add-book-modal", item);
this.favouriteBooks.splice(index, 1);
}
if (item.category === "Best of the best") {
eventBus.$emit("open-add-book-modal", item);
this.bestOfTheBest.splice(index, 1);
}
}
}
Recuerde, BookModal está emitiendo, y un evento llamado save-book, necesitamos un oyente para ese evento aquí.
created() {
eventBus.$on("save-book", cardData => {
if (cardData.category === "Recently read books") {
this.recentBooks.push(cardData);
}
if (cardData.category === "Favourite books") {
this.favouriteBooks.push(cardData);
}
if (cardData.category === "Best of the best") {
this.bestOfTheBest.push(cardData);
}
});
},
Ahora, en un vistazo completo, nuestra página index.vue se ve así
<template>
<div>
<v-row>
<v-col md="4">
<h2 class="text-center mb-5">Recently Read Books</h2>
<v-row v-for="(item,index) in recentBooks" :key="index">
<BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<template v-slot:button>
<nuxt-link :to="`/books/${item.title}`">
<v-btn>View</v-btn>
</nuxt-link>
<v-btn @click.stop="edit(item,index)">Edit</v-btn>
<v-btn @click.stop="remove(item.category, index)">Remove</v-btn>
</template>
</BookCard>
</v-row>
</v-col>
<v-col md="4">
<h2 class="text-center mb-5">Favourite Books</h2>
<v-row v-for="(item,index) in favouriteBooks" :key="index">
<BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<template v-slot:button>
<v-btn @click.stop="edit(item,index)">Edit</v-btn>
<v-btn @click.stop="remove(item.category, index)">Remove</v-btn>
</template>
</BookCard>
</v-row>
</v-col>
<v-col md="4">
<h2 class="text-center mb-5">Best of the Best</h2>
<v-row v-for="(item,index) in bestOfTheBest" :key="index">
<BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<template v-slot:button>
<v-btn @click.stop="edit(item,index)">Edit</v-btn>
<v-btn @click.stop="remove(item.category, index)">Remove</v-btn>
</template>
</BookCard>
</v-row>
</v-col>
</v-row>
<BookModal />
</div>
</template>
<script>
import BookCard from "@/components/BookCard";
import BookModal from "@/components/BookModal";
import { eventBus } from "@/eventBus";
export default {
components: {
BookCard,
BookModal
},
data() {
return {
recentBooks: [],
favouriteBooks: [],
bestOfTheBest: []
};
},
created() {
eventBus.$on("save-book", cardData => {
if (cardData.category === "Recently read books") {
this.recentBooks.push(cardData);
this.recentBooks.sort((a, b) => b - a);
}
if (cardData.category === "Favourite books") {
this.favouriteBooks.push(cardData);
this.favouriteBooks.sort((a, b) => b - a);
}
if (cardData.category === "Best of the best") {
this.bestOfTheBest.push(cardData);
this.bestOfTheBest.sort((a, b) => b - a);
}
});
},
methods: {
remove(category, index) {
if (category === "Recently read books") {
this.recentBooks.splice(index, 1);
}
if (category === "Favourite books") {
this.favouriteBooks.splice(index, 1);
}
if (category === "Best of the best") {
this.bestOfTheBest.splice(index, 1);
}
},
edit(item, index) {
if (item.category === "Recently read books") {
eventBus.$emit("open-add-book-modal", item);
this.recentBooks.splice(index, 1);
}
if (item.category === "Favourite books") {
eventBus.$emit("open-add-book-modal", item);
this.favouriteBooks.splice(index, 1);
}
if (item.category === "Best of the best") {
eventBus.$emit("open-add-book-modal", item);
this.bestOfTheBest.splice(index, 1);
}
}
}
};
</script>
Si llegaste tan lejos, ¡¡¡Buen trabajo !!! ¡Usted es maravilloso!
Como se mencionó anteriormente, cada archivo .vue creado en la carpeta de páginas se aprovisiona automáticamente como una ruta, del mismo modo, para cada carpeta creada dentro de la carpeta de páginas. Esto no solo es válido para páginas estáticas, ¡sino que también se pueden crear páginas dinámicas de esta manera!
A ver cómo.
Usando nuestro proyecto actual, digamos que queremos agregar una página dinámica para todas las tarjetas de libros con un botón de visualización para ver más detalles sobre un libro.
Agreguemos rápidamente un botón de vista y usemos un <nuxt-link>
para visitar la página. Si, <nuxt-link>
reemplaza <router-link>
y funciona.
<nuxt-link :to="`/${item.title}`">
<v-btn>View</v-btn>
</nuxt-link>
A continuación, creamos una carpeta dinámica prefijando el nombre con un guión bajo. es decir, _title y dentro de esa carpeta, tendremos un archivo index.vue que se renderiza cuando visitamos esa ruta.
Solo para demostración, solo accederemos a la propiedad params dentro del archivo.
// _title/index.vue
<template>
<h1>{{$route.params.title}}</h1>
</template>
Ahora, al hacer clic en ver, se abre otra página donde podemos ver el título que hemos pasado por la ruta. Esto se puede desarrollar para hacer lo que queramos en lo que respecta a las páginas dinámicas.
¡Eso es todo por esta lección!
El código completo para esto se puede encontrar en este repositorio. Le invitamos a contribuir al código. Si está interesado en dominar el marco, le sugiero esto Curso Udemy.