Optimización de aplicaciones Next.js con Nx

Nx es un marco de compilación que facilita la optimización, el escalado eficiente de aplicaciones y otras características como bibliotecas y componentes compartidos. En este artículo, veremos cómo podemos escalar eficazmente las aplicaciones Next.js utilizando Nx.
En este artículo, veremos cómo optimizar y crear una aplicación Next.js de alto rendimiento utilizando Nx y sus ricas funciones. Veremos cómo configurar un servidor Nx, cómo agregar un complemento a un servidor existente y el concepto de monorepo con una visualización práctica.
Si es un desarrollador que busca optimizar aplicaciones y crear componentes reutilizables en todas las aplicaciones de manera efectiva, este artículo le mostrará cómo escalar rápidamente sus aplicaciones y cómo trabajar con Nx. Para seguir adelante, necesitará conocimientos básicos del marco Next.js y TypeScript.
¿Qué es Nx?
Nx es un marco de construcción de código abierto que le ayuda a diseñar, probar y construir a cualquier escala, integrándose perfectamente con tecnologías y bibliotecas modernas, al tiempo que proporciona una sólida interfaz de línea de comandos (CLI), almacenamiento en caché y gestión de dependencias. Nx ofrece a los desarrolladores herramientas CLI avanzadas y complementos para marcos, pruebas y herramientas modernos.
En este artículo, nos centraremos en cómo funciona Nx con las aplicaciones Next.js. Nx proporciona herramientas estándar para probar y diseñar sus aplicaciones Next.js, como Cypress, Storybook y componentes con estilo. Nx facilita un monorepo para sus aplicaciones, creando un espacio de trabajo que puede contener el código fuente y las bibliotecas de múltiples aplicaciones, lo que le permite compartir recursos entre aplicaciones.
¿Por qué utilizar Nx?
Nx proporciona a los desarrolladores una cantidad razonable de funcionalidad lista para usar, incluidos textos estándar para pruebas de extremo a extremo (E2E) de su aplicación, una biblioteca de estilos y un monorepo.
El uso de Nx conlleva muchas ventajas y analizaremos algunas de ellas en esta sección.
- Ejecución de tareas basada en gráficos
Nx utiliza la ejecución distribuida de tareas basada en gráficos y el almacenamiento en caché de cálculo para acelerar las tareas. El sistema programará tareas y comandos utilizando un sistema gráfico para determinar qué nodo (es decir, aplicación) debe ejecutar cada tarea. Esto maneja la ejecución de aplicaciones y optimiza el tiempo de ejecución de manera eficiente. - Testing
Nx proporciona herramientas de prueba preconfiguradas para pruebas unitarias y pruebas E2E. - Caching
Nx también almacena el gráfico del proyecto en caché. Esto le permite volver a analizar solo los archivos actualizados. Nx realiza un seguimiento de los archivos modificados desde la última confirmación y le permite probar, crear y realizar acciones sólo en esos archivos; esto permite una optimización adecuada cuando se trabaja con una base de código grande. - Gráfico de dependencia
El gráfico de dependencia visual le permite inspeccionar cómo interactúan los componentes entre sí. - Almacenamiento en la nube
Nx también proporciona almacenamiento en la nube e integración con GitHub, para que pueda compartir enlaces con los miembros del equipo para revisar los registros del proyecto. - Compartir código
Crear una nueva biblioteca compartida para cada proyecto puede resultar bastante agotador. Nx elimina esta complicación, permitiéndole concentrarse en la funcionalidad principal de su aplicación. Con Nx, puede compartir bibliotecas y componentes entre aplicaciones. Incluso puede compartir código reutilizable entre sus aplicaciones de front-end y back-end. - La compatibilidad con monorepos
Nx proporciona un espacio de trabajo para múltiples aplicaciones. Con esta configuración, un repositorio de GitHub puede albergar el código fuente de varias aplicaciones en su espacio de trabajo.
Nx para bibliotecas publicables
Nx le permite crear bibliotecas publicables. Esto es esencial cuando tienes bibliotecas que usarás fuera del monorepo. En cualquier caso en el que esté desarrollando componentes de interfaz de usuario organizacional con la integración de Nx Storybook, Nx creará componentes publicables junto con sus historias. Los componentes publicables pueden compilar estos componentes para crear un paquete de biblioteca que puede implementar en un registro externo. Usarías la --publishable
opción al generar la biblioteca, a diferencia de --buildable
, que se usa para generar bibliotecas que se usan solo en el monorepo. Nx no implementa las bibliotecas publicables automáticamente; puede invocar la compilación mediante un comando como nx build mylib
(dónde mylib
está el nombre de la biblioteca), que luego producirá un paquete optimizado en la carpeta dist
/ mylib
que se puede implementar en un registro externo.
Nx le ofrece la opción de crear un nuevo espacio de trabajo con Next.js como valor preestablecido o agregar Next.js a un espacio de trabajo existente.
Para crear un nuevo espacio de trabajo con Next.js como ajuste preestablecido, puede usar el siguiente comando:
npx create-nx-workspace happynrwl --preset=next --style=styled-components --appName=todo
Este comando creará un nuevo espacio de trabajo de Nx con una aplicación Next.js llamada "todo" y styled-components
como biblioteca de estilos.
Luego, podemos agregar la aplicación Next.js a un espacio de trabajo de Nx existente con el siguiente comando:
npx nx g @nrwl/next:app
Creación de una aplicación Next.js y Nx
El complemento Nx para Next.js incluye herramientas y ejecutores para ejecutar y optimizar una aplicación Next.js. Para comenzar, necesitamos crear un nuevo espacio de trabajo de Nx con next
un valor preestablecido:
npx create-nx-workspace happynrwl --preset=next --style=styled-components --appName=todo
El bloque de código anterior generará un nuevo espacio de trabajo de Nx y la aplicación Next.js. Recibiremos un mensaje para usar Nx Cloud. Para este tutorial, seleccionaremos "No" y luego esperaremos a que se instalen nuestras dependencias. Una vez hecho esto, deberíamos tener un árbol de archivos similar a este:
happynrwl ┣ apps ┃ ┣ todo ┃ ┣ todo-e2e ┃ ┗ .gitkeep ┣ libs ┣ node_modules ┣ tools ┣ .editorconfig ┣ .eslintrc.json ┣ .gitignore ┣ .prettierignore ┣ .prettierrc ┣ README.md ┣ babel.config.json ┣ jest.config.js ┣ jest.preset.js ┣ nx.json ┣ package-lock.json ┣ package.json ┣ tsconfig.base.json ┗ workspace.json
En la apps
carpeta tendremos nuestra aplicación Next.js “todo”, con la prueba E2E preconfigurada para la aplicación de tareas pendientes. Todo esto se genera automáticamente con la poderosa herramienta Nx CLI.
Para ejecutar nuestra aplicación, use el npx nx serve todo
comando. Una vez que haya terminado de servir la aplicación, debería ver la siguiente pantalla:
Construyendo la API
En este punto, hemos configurado el espacio de trabajo. Lo siguiente es construir la API CRUD que usaremos en la aplicación Next.js. Para hacer esto, usaremos Express; Para demostrar la compatibilidad con monorepo, construiremos nuestro servidor como una aplicación en el espacio de trabajo. Primero, tenemos que instalar el complemento Express para Nx ejecutando este comando:
npm install --save-dev @nrwl/express
Una vez hecho esto, estamos listos para configurar nuestra aplicación Express en el espacio de trabajo proporcionado. Para generar una aplicación Express, ejecute el siguiente comando:
npx nx g @nrwl/express:application --name=todo-api --frontendProject=todo
El comando nx g @nrwl/express:application
generará una aplicación Express a la que podemos pasar parámetros de especificación adicionales; para especificar el nombre de la aplicación, use la --name
bandera; para indicar la aplicación front-end que utilizará la aplicación Express, pase el nombre de una aplicación en nuestro espacio de trabajo a --frontendProject
. Algunas otras opciones están disponibles para una aplicación Express . Una vez hecho esto, tendremos una estructura de archivos actualizada en la apps
carpeta con la todo-api
carpeta agregada.
happynrwl ┣ apps ┃ ┣ todo ┃ ┣ todo-api ┃ ┣ todo-e2e ┃ ┗ .gitkeep …
La todo-api
carpeta es un modelo estándar Express con un main.ts
archivo de entrada.
/** * This is not a production server yet! * This is only minimal back end to get started. */import * as express from 'express';import {v4 as uuidV4} from 'uuid';const app = express();app.use(express.json()); // used instead of body-parserapp.get('/api', (req, res) = { res.send({ message: 'Welcome to todo-api!' });});const port = process.env.port || 3333;const server = app.listen(port, () = { console.log(`Listening at http://localhost:${port}/api`);});server.on('error', console.error);
Crearemos nuestras rutas dentro de esta aplicación. Para comenzar, inicializaremos una matriz de objetos con dos pares clave-valor item
y id
justo debajo de la declaración de la aplicación.
/** * This is not a production server yet! * This is only minimal back end to get started. */import * as express from 'express';import {v4 as uuidV4} from 'uuid';const app = express();app.use(express.json()); // used instead of body-parserlet todoArray: Array{ item: string; id: string } = [ { item: 'default todo', id: uuidV4() },];…
A continuación, configuraremos la ruta para recuperar todas las listas de tareas pendientes en app.get()
:
…app.get('/api', (req, res) = { res.status(200).json({ data: todoArray, });});…
El bloque de código anterior devolverá el valor actual de todoArray
. Posteriormente, tendremos rutas para crear, actualizar y eliminar elementos de tareas pendientes de la matriz.
…app.post('/api', (req, res) = { const item: string = req.body.item; // Increment ID of item based on the ID of the last item in the array. let id: string = uuidV4(); // Add the new object to the array todoArray.push({ item, id }); res.status(200).json({ message: 'item added successfully', });});app.patch('/api', (req, res) = { // Value of the updated item const updatedItem: string = req.body.updatedItem; // ID of the position to update const id: string = req.body.id; // Find index of the ID const arrayIndex = todoArray.findIndex((obj) = obj.id === id); // Update item that matches the index todoArray[arrayIndex].item = updatedItem res.status(200).json({ message: 'item updated successfully', });});app.delete('/api', (req, res) = { // ID of the position to remove const id: string = req.body.id; // Update array and remove the object that matches the ID todoArray = todoArray.filter((val) = val.id !== id); res.status(200).json({ message: 'item removed successfully', });});…
Para crear una nueva tarea pendiente, todo lo que necesitamos es el valor del nuevo elemento como una cadena. Generaremos una ID incrementando la ID del último elemento de la matriz en el servidor. Para actualizar un elemento existente, pasaríamos el nuevo valor del elemento y el ID del objeto del elemento que se actualizará; en el servidor, recorreríamos cada elemento con el forEach
método y actualizaríamos el elemento en el lugar donde la ID coincide con la ID enviada con la solicitud. Finalmente, para eliminar un elemento de la matriz, enviaríamos el ID del elemento que se eliminará con la solicitud; luego, filtramos la matriz y devolvemos una nueva matriz de todos los elementos que no coinciden con el ID enviado con la solicitud, asignando la nueva matriz a la todoArray
variable.
Nota: Si busca en la carpeta de la aplicación Next.js, debería ver un proxy.conf.json
archivo con la siguiente configuración:
{ "/api": { "target": "http://localhost:3333", "secure": false }}
Esto crea un proxy, lo que permite que todas las llamadas API a rutas coincidentes /api
se dirijan al todo-api
servidor.
Generando páginas Next.js con Nx
En nuestra aplicación Next.js, generaremos una nueva página home
y un componente de elemento. Nx proporciona una herramienta CLI para que podamos crear fácilmente una página:
npx nx g @nrwl/next:page home
Al ejecutar este comando, se nos pedirá que seleccionemos la biblioteca de estilos que queremos usar para la página; Para este artículo, seleccionaremos styled-components
. ¡Voilá! Nuestra página está creada. Para crear un componente, ejecute npx nx g @nrwl/next:component todo-item
; esto creará una component
carpeta con el todo-item
componente.
Consumo de API en la aplicación Next.js
En cada tarea pendiente, tendremos dos botones, para editar y eliminar la tarea pendiente. Las funciones asincrónicas que realizan estas acciones se pasan como accesorios desde la página de inicio.
…export interface TodoItemProps { updateItem(id: string, updatedItem: string): Promisevoid; deleteItem(id: string): Promisevoid; fetchItems(): Promiseany; item: string; id: string;}export const FlexWrapper = styled.div` width: 100%; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #ccc; padding-bottom: 10px; margin-top: 20px; @media all and (max-width: 470px) { flex-direction: column; input { width: 100%; } button { width: 100%; } }`;export function TodoItem(props: TodoItemProps) { const [isEditingItem, setIsEditingItem] = useStateboolean(false); const [item, setNewItem] = useStatestring | null(null); return ( FlexWrapper Input disabled={!isEditingItem} defaultValue={props.item} isEditing={isEditingItem} onChange={({ target }) = setNewItem(target.value)} / {!isEditingItem Button onClick={() = setIsEditingItem(true)} Edit /Button} {isEditingItem Button onClick={async () = { await props.updateItem(props.id, item); //fetch updated items await props.fetchItems(); setIsEditingItem(false) }} Update /Button} Button danger onClick={async () = { await props.deleteItem(props.id); //fetch updated items await await props.fetchItems(); }} Delete /Button /FlexWrapper );}
Para la funcionalidad de actualización, tenemos una entrada que está deshabilitada cuando el isEditingItem
estado es false
. Una vez que se hace clic en el botón "Editar", cambia el isEditingItem
estado true
y muestra el botón "Actualizar". Aquí, el componente de entrada está habilitado y el usuario puede ingresar un nuevo valor; cuando se hace clic en el botón "Actualizar", llama a la updateItem
función con los parámetros pasados y vuelve a isEditingItem
cambiar a false
.
En el home
componente de página, tenemos las funciones asincrónicas que realizan la operación CRUD.
… const [items, setItems] = useStateArray{ item: string; id: string }([]); const [newItem, setNewItem] = useStatestring(''); const fetchItems = async () = { try { const data = await fetch('/api/fetch'); const res = await data.json(); setItems(res.data); } catch (error) { console.log(error); } }; const createItem = async (item: string) = { try { const data = await fetch('/api', { method: 'POST', body: JSON.stringify({ item }), headers: { 'Content-Type': 'application/json', }, }); } catch (error) { console.log(error); } }; const deleteItem = async (id: string) = { try { const data = await fetch('/api', { method: 'DELETE', body: JSON.stringify({ id }), headers: { 'Content-Type': 'application/json', }, }); const res = await data.json(); alert(res.message); } catch (error) { console.log(error); } }; const updateItem = async (id: string, updatedItem: string) = { try { const data = await fetch('/api', { method: 'PATCH', body: JSON.stringify({ id, updatedItem }), headers: { 'Content-Type': 'application/json', }, }); const res = await data.json(); alert(res.message); } catch (error) { console.log(error); } }; useEffect(() = { fetchItems(); }, []);…
En el bloque de código anterior, tenemos fetchItems
, que regresa todoArray
del servidor. Luego tenemos la createItem
función, que toma una cadena; el parámetro es el valor de la nueva tarea pendiente. La updateItem
función toma dos parámetros, el ID del elemento a actualizar y el updatedItem
valor. Y la deleteItem
función elimina el elemento que coincide con el ID que se pasa.
Para representar la tarea pendiente, asignamos el items
estado:
…return ( StyledHome h1Welcome to Home!/h1 TodoWrapper {items.length 0 items.map((val) = ( TodoItem key={val.id} item={val.item} id={val.id} deleteItem={deleteItem} updateItem={updateItem} fetchItems={fetchItems} / ))} /TodoWrapper form onSubmit={async(e) = { e.preventDefault(); await createItem(newItem); //Clean up new item setNewItem(''); await fetchItems(); }} FlexWrapper Input value={newItem} onChange={({ target }) = setNewItem(target.value)} placeholder="Add new item…" / Button success type="submit" Add + /Button /FlexWrapper /form /StyledHome );…
Nuestro servidor y nuestra interfaz ya están configurados. Podemos servir la aplicación API ejecutando npx nx serve todo-api
y, para la aplicación Next.js, ejecutamos npx nx serve todo
. Haga clic en el botón "Continuar" y verá una página con la tarea pendiente predeterminada.
Ahora tenemos una aplicación Next.js y Express funcionando juntas en un solo espacio de trabajo.
Nx tiene otra herramienta CLI que nos permite ver el gráfico de dependencia de nuestra aplicación en nuestra ejecución de terminal. Ejecute npx nx dep-graph
y deberíamos ver una pantalla similar a la imagen a continuación, que muestra el gráfico de dependencia de nuestra aplicación.
Otros comandos CLI para Nx
nx list
Enumera los complementos de Nx actualmente instalados.nx migrate latest
Actualiza los paquetespackage.json
a la última versión.nx affected
Realiza la acción solo en las aplicaciones afectadas o modificadas.nx run-many --target serve --projects todo-api,todo
Ejecuta el comando de destino en todos los proyectos enumerados.
Conclusión
Como descripción general de Nx, este artículo ha cubierto lo que ofrece Nx y cómo nos facilita el trabajo. También explicamos cómo configurar una aplicación Next.js en un espacio de trabajo de Nx, agregar un complemento Express a un espacio de trabajo existente y usar la función monorepo para albergar más de una aplicación en nuestro espacio de trabajo.
Encontrarás el código fuente completo en el repositorio de GitHub . Para obtener información adicional sobre Nx, consulte la documentación o la documentación de Nx para Next.js.
(ks, vf, yk, il, al)Explora más en
- Reaccionar
- javascript
- Aplicaciones
- Mecanografiado
- Herramientas
Deja un comentario