BackVolver al blog

Next.js 8

Next.js 8 introduce el Modo Serverless, paquetes más pequeños, mejoras de rendimiento y más.

Hoy nos enorgullece presentar Next.js 8 listo para producción, que incluye:

Como siempre, nos hemos esforzado por garantizar que todos estos beneficios sean completamente compatibles con versiones anteriores. Para la mayoría de las aplicaciones Next.js, solo necesita ejecutar:

Terminal
npm i next@latest react@latest react-dom@latest

Estamos agradecidos con nuestra comunidad y todos los que han confiado en nuestro éxito. Desde nuestra última publicación en el blog, hemos visto empresas como AT&T, Starbucks y Twitch relanzar sus sitios web y aplicaciones públicas con Next.js.

Next.js Serverless

El objetivo serverless de Next.js genera funciones Serverless a partir de páginas

El despliegue serverless mejora drásticamente la confiabilidad y escalabilidad al dividir su aplicación en partes más pequeñas (también llamadas lambdas). En el caso de Next.js, cada página en el directorio pages se convierte en una lambda serverless.

Hay varios beneficios al usar serverless. El enlace mencionado habla de algunos de ellos en el contexto de Express, pero los principios se aplican universalmente: serverless permite puntos de falla distribuidos, escalabilidad infinita y es increíblemente asequible con un modelo de "pago por lo que usa".

Para habilitar el modo serverless en Next.js, agregue el target de compilación serverless en next.config.js:

next.config.js
module.exports = {
  target: 'serverless',
};

El objetivo serverless generará una lambda por página. Este archivo es completamente independiente y no requiere ninguna dependencia para ejecutarse:

  • pages/index.js => .next/serverless/pages/index.js
  • pages/about.js => .next/serverless/pages/about.js

La firma de la función Serverless de Next.js es similar al callback del servidor HTTP de Node.js:

type Function = (req: http.IncomingMessage, res: http.ServerResponse) => void;
  • http.IncomingMessage
  • http.ServerResponse
  • void se refiere a que la función no tiene un valor de retorno y es equivalente a undefined de JavaScript. Llamar a la función finalizará la solicitud.

Next.js proporciona APIs de bajo nivel para despliegues serverless ya que las plataformas de hosting tienen diferentes firmas de funciones. En general, querrás envolver la salida de una compilación serverless de Next.js con una capa de compatibilidad.

Por ejemplo, si la plataforma soporta la clase http.Server de Node.js:

server.js
const http = require('http');
const page = require('./.next/serverless/about.js');
const server = new http.Server((req, res) => page.render(req, res));
server.listen(3000, () => console.log('Listening on http://localhost:3000'));

Resumen

  • API de bajo nivel para implementar despliegue serverless
  • Cada página en el directorio pages se convierte en una función serverless (lambda)
  • Crea la función serverless más pequeña posible (50 KB tamaño base zip)
  • Optimizado para un inicio rápido en frío de la función
  • La función serverless tiene 0 dependencias (se incluyen en el paquete de la función)
  • Usa http.IncomingMessage y http.ServerResponse de Node.js
  • Se activa usando target: 'serverless' en next.config.js
  • El objetivo server sigue siendo totalmente compatible y mantenido
  • publicRuntimeConfig y serverRuntimeConfig no son compatibles en el modo serverless. Use configuración en tiempo de compilación en su lugar.

Reducción masiva del uso de memoria en tiempo de compilación

Hemos contribuido a webpack para mejorar el rendimiento de compilación y la utilización de recursos de Next.js (¡y del resto del ecosistema webpack!).

Este esfuerzo ha resultado en un uso de memoria hasta 16 veces mejor sin degradación en el rendimiento.

La memoria se libera mucho más rápido y los procesos ya no se bloquean bajo mucho estrés (muchas páginas).

Pronto profundizaremos en cómo hemos logrado esta optimización. Esté atento al blog de Next.js.

Configuración de entorno en tiempo de compilación

Al revisar aplicaciones Next.js, un patrón recurrente que observamos fue agregar babel-plugin-transform-define o webpack.DefinePlugin para proporcionar valores de configuración a la aplicación.

Con Next.js 8 estamos introduciendo una nueva clave en next.config.js llamada env para proporcionar la misma funcionalidad de manera compatible con versiones anteriores:

next.config.js
module.exports = {
  env: {
    customKey: 'MyValue',
  },
};

Esto le permitirá usar process.env.customKey en su código. Por ejemplo:

pages/index.js
export default function IndexPage() {
  return <h1>El valor de customKey es: {process.env.customKey}</h1>;
}

process.env.customKey será reemplazado con 'MyValue' en tiempo de compilación.

Mejoras en el rendimiento de prefetch

El enrutador de Next.js le permite precargar páginas para una navegación más rápida:

pages/index.js
import Link from 'next/link';
 
export default function IndexPage() {
  return (
    <>
      <Link href="/about" prefetch>
        <a>Ir a About Page</a>
      </Link>
    </>
  );
}

Funciona precargando el paquete JavaScript de cada enlace que tiene un atributo prefetch.

En versiones anteriores a Next.js 8 esto significaba inyectar una etiqueta <script> en el <body> del documento.

Sin embargo, esto introdujo cierta sobrecarga al abrir páginas, más notablemente la indicación de "cargando" del navegador se mostraba por más tiempo del esperado, incluso cuando la página ya podía interactuarse.

En Next.js 8 prefetch usa <link rel="preload"> en lugar de una etiqueta <script>. Además, solo comienza a precargar después de onload para permitir que el navegador administre los recursos.

Adicionalmente, Next.js ahora detecta conexiones 2G y el modo navigator.connection.saveData para desactivar prefetch en conexiones de red más lentas.

Tamaño HTML inicial más pequeño

Como Next.js pre-renderiza HTML, envuelve las páginas en una estructura predeterminada con <html>, <head>, <body> y los archivos JavaScript necesarios para renderizar la página.

Con Next.js 7 optimizamos la carga inicial a 1.50KB, lo que fue una reducción del 7.4% respecto a la versión anterior.

Hemos podido reducir aún más el tamaño de la carga inicial a 1.16KB, una reducción adicional del 23%:

7.08.0delta
Tamaño del documento (renderizado en servidor)1.50KB1.16KB23% más pequeño

Las principales formas en que hemos reducido el tamaño son:

  • Se eliminó el script inline de inicialización de página
  • La página /_error ya no se incluye en cada carga de página

Carga bajo demanda de /_error

Cuando ocurre un error en producción, se renderiza la página /_error para mostrar que ocurrió un error.

Desde el primer lanzamiento de Next.js, la etiqueta de script de /_error era parte del HTML inicial, lo que significaba que se cargaba incluso cuando no se usaría si no hubiera errores en tiempo de ejecución.

A partir de Next.js 8, la página /_error se carga bajo demanda cuando ocurre un error.

Esto significa que hay menos código para cargar, analizar y ejecutar por defecto.

Mejoras en la experiencia de desarrollo

Uno de los objetivos principales de Next.js es proporcionar el mejor rendimiento en producción con la mejor experiencia de desarrollo posible. Esta versión incluye muchas mejoras sutiles basadas en comentarios de los usuarios.

Entradas bajo demanda mejoradas

De forma predeterminada, Next.js compila automáticamente solo las páginas que se están desarrollando activamente. Next.js no compila todas las páginas en el directorio pages cada vez que se ejecuta next dev. En su lugar, compila páginas a medida que accede a ellas.

Por ejemplo, al visitar http://localhost:3000/my-page, el archivo pages/my-page.js se compila bajo demanda, después de lo cual se renderiza la página.

Esto garantiza que el desarrollador no tenga que esperar a que se compilen todas las páginas al iniciar el servidor de desarrollo, lo que puede llevar bastante tiempo en aplicaciones más grandes. Mantiene bajo el uso de memoria y el compilador rápido, ya que el compilador no necesita tener en cuenta todas las páginas al agrupar.

El flujo de entradas bajo demanda

El flujo de entradas bajo demanda

Cuando una página no se ha accedido durante 25 segundos, se eliminará de la caché de compilación del compilador para mantener el compilador rápido y reducir el uso de memoria.

La forma en que Next.js realiza un seguimiento de las páginas a las que se accede es mediante un mecanismo de sondeo. Cada 5 segundos, se envía un "ping de entradas bajo demanda" para que el servidor de desarrollo de Next.js sepa que se está accediendo a una página determinada.

Desde el lanzamiento inicial de esta característica, el ping se realizaba mediante una llamada window.fetch, lo que significaba que cada vez que se activaba el ping, aparecía en las herramientas de desarrollo del navegador en las pestañas console y network.

Una de las características más solicitadas es la capacidad de ocultar estas solicitudes de las herramientas de desarrollo del navegador, ya que pueden agregar ruido innecesario.

Nos complace anunciar que en Next.js 8, el ping basado en fetch ha sido reemplazado por un enfoque basado en WebSockets, lo que significa que los pings aún ocurren pero solo son visibles al inspeccionar la conexión WebSocket.

Un agradecimiento especial a JJ Kasper por colaborar en la conversión a WebSockets.

Escucha de puertos más rápida en desarrollo

Al iniciar el servidor de desarrollo de Next.js, debe ejecutar una compilación inicial para poder atender solicitudes. Por defecto, Next.js esperaría que este paso de compilación terminara antes de iniciar el servidor HTTP, lo que significaba que si ejecutaba next dev y luego iba a su navegador, a veces podía aparecer el mensaje "Este sitio no se puede alcanzar" porque el servidor HTTP aún no estaba escuchando conexiones.

Con Next.js 8, el servidor HTTP estará escuchando conexiones antes de que comience la compilación, lo que significa que si va a http://localhost:3000/ antes de que finalice la compilación, la solicitud esperará a que finalice la compilación inicial antes de servir la solicitud, en lugar de tener que actualizar la página hasta que esté disponible.

Un agradecimiento especial a Brian Beck por implementar esta característica.

Exportación estática más rápida

Next.js se centra en la idea de pre-renderizado como medio para lograr un alto rendimiento. El pre-renderizado viene en dos formas:

  • Renderizado en servidor donde cada solicitud activa un renderizado. Como resultado, el usuario final no tiene que esperar a que se descargue ningún JS para comenzar a consumir datos
  • Renderizado estático donde generamos archivos estáticos que se pueden servir directamente sin ninguna ejecución de código en el servidor

A partir de Next.js 8, el renderizado estático a través de next export tendrá mejoras de velocidad si su máquina tiene múltiples CPUs.

Según pruebas con un MacBook de 4 núcleos de CPU, la velocidad de exportación pasó de aproximadamente 25 páginas por segundo a 75 páginas por segundo al aprovechar todos los núcleos para pre-renderizar páginas.

Next.js detectará automáticamente el número de núcleos de CPU y distribuirá las páginas en consecuencia, sin necesidad de cambios en el código.

Un agradecimiento especial a Benjamin Kniffler por implementar esta característica.

Deduplicación de elementos Head

Una necesidad común al construir aplicaciones es actualizar el elemento <head> de una página. Por ejemplo, para establecer el <title> o <meta name="viewport"> para diseño responsivo.

Next.js expone un componente incorporado para introducir cambios en el <head>:

pages/index.js
import Head from 'next/head';
 
export default function IndexPage() {
  return (
    <>
      <Head>
        <title>Mi título de página</title>
      </Head>
    </>
  );
}

El componente <Head> incluso puede usarse múltiples veces en diferentes componentes, por ejemplo, su componente de diseño podría establecer algunas etiquetas head predeterminadas.

Sin embargo, es posible que desee anular las etiquetas head predeterminadas con un valor diferente. En versiones anteriores de Next.js, esto haría que la etiqueta se duplicara en la salida, ya que no había forma de deduplicar etiquetas.

Por esta razón, ahora es posible proporcionar una propiedad key a cada elemento dentro del componente <Head> que deduplicará automáticamente etiquetas con el mismo valor key.

Al establecer key="viewport" en dos etiquetas, solo se renderiza la última.

pages/index.js
import Head from 'next/head';
export default function IndexPage() {
  return (
    <>
      <Head>
        <title>Mi título de página</title>
        <meta
          name="viewport"
          content="initial-scale=1.0, width=device-width"
          key="viewport"
        />
      </Head>
      <Head>
        <meta
          name="viewport"
          content="initial-scale=1.2, width=device-width"
          key="viewport"
        />
      </Head>
    </>
  );
}

Mejoras de seguridad

Nueva opción de configuración crossOrigin

En Next.js 6 introdujimos la opción de agregar un atributo crossOrigin a <Head> y <NextScript> en pages/_document.js, sin embargo esto no cubría todos los casos de uso para establecer cross-origin.

Next.js tiene un enrutador del lado del cliente que inyecta dinámicamente etiquetas <script>, a estas etiquetas les faltaba el atributo cross-origin cuando se inyectaban.

Para garantizar que todas las etiquetas <script> tengan configurado cross-origin, hemos introducido una nueva opción de configuración en next.config.js

next.config.js
module.exports = {
  crossOrigin: 'anonymous',
};

Otro beneficio de introducir esta opción es que ya no se necesita un pages/_document.js personalizado para configurar cross-origin en su aplicación.

El comportamiento anterior sigue siendo compatible pero emitirá una advertencia en desarrollo para ayudar a los desarrolladores a migrar a la opción recién introducida.

Eliminación de Javascript en línea

Al usar Next.js 7 y versiones anteriores, para habilitar la Política de Seguridad de Contenido (CSP), el usuario debía incluir script-src 'unsafe-inline' en su política porque Next.js creaba una etiqueta <script> en línea para pasar datos, por ejemplo, para pasar el resultado de getInitialProps al lado del cliente.

Con Next.js 8 hemos cambiado esta etiqueta de script en línea por una etiqueta JSON para una transferencia segura al cliente. Esto significa que Next.js ya no incluye scripts en línea.

Con una consideración cuidadosa, ahora se puede usar script-src 'self'.

Ejemplo de autenticación API

Uno de los ejemplos más solicitados de todos los tiempos ha sido cómo hacer autenticación en Next.js contra una API externa, cualquier API, en cualquier lenguaje de programación.

Con la introducción de Next.js 8, también presentamos un ejemplo recién creado: with-cookie-auth

Este ejemplo muestra cómo autenticarse contra una API Node.js externa, pero los conceptos aplicados funcionan para cualquier API sin estado (stateless).

El ejemplo utiliza una cookie para compartir el token entre el renderizado del lado del servidor (server-side) y el lado del cliente (client-side).

De esta manera, si la aplicación se renderiza en el servidor, aún puede obtener datos autenticados en nombre del usuario.

Un agradecimiento especial a Juan Olvera quien contribuyó con el ejemplo.

Comunidad

Desde su primer lanzamiento, Next.js ha sido utilizado en todo, desde empresas Fortune 500 hasta blogs personales. Estamos muy emocionados de ver el crecimiento continuo en la adopción de Next.js.

  • Hemos tenido más de 600 colaboradores que han realizado al menos 1 commit.
  • En GitHub, el proyecto ha recibido más de 34,400 estrellas.
  • Se han enviado más de 2600 pull requests desde el primer lanzamiento.

La comunidad de Next.js tiene más de 4,570 miembros. ¡Únase a nosotros!