BackVolver al blog

Next.js 5: Webpack Universal, Importación de CSS, Plugins y Zonas

Next.js 5 se enfoca en mayor extensibilidad, composabilidad para aplicaciones grandes y rendimiento

Estamos muy contentos de presentar Next.js 5.0 al mundo. Ya está disponible en npm de inmediato. Para actualizar, ejecute:

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

Además de actualizar Next.js, también actualizamos las dependencias peer react y react-dom

Next.js es un kit de herramientas para aplicaciones React.js universales, renderizadas en el servidor (o pre-renderizadas estáticamente). Comenzar a desarrollar una aplicación de cualquier tamaño es tan fácil como ejecutar next. (Leer más.)

Con cada nueva versión nos comprometemos a mantener la compatibilidad con versiones anteriores, ofrecer rutas de actualización simples y solo realizar cambios en la API cuando sea absolutamente necesario. Next.js 5.0 no es una excepción.

Sin embargo, bajo el capó, Next.js ha sufrido una transformación radical para habilitar nuevos casos de uso potentes y extensibilidad. Comenzamos haciendo que Next.js comparta una canalización universal de Webpack tanto para el código del servidor como del cliente.

Webpack Universal y Plugins de Next

Next.js aprovecha herramientas poderosas existentes como Webpack, Babel y Uglify, presentadas al usuario final como una interfaz notablemente simple: next (para desarrollar), next build (para preparar para producción) y next start (para servir) o next export para pre-renderizar a archivos estáticos.

Una de las primeras decisiones que tomamos fue proporcionar puntos de extensibilidad muy potentes para cómo se configuran estas herramientas. No solo queríamos facilidad de uso, queríamos permitir flexibilidad para extender el kit de herramientas como quisieras.

Por ejemplo, puedes extender la configuración de webpack de Next.js configurando una propiedad webpack en tu next.config.js.

Debido a que webpack se ejecuta de manera diferente para producción y desarrollo, decidimos en ese momento hacerlo una función que decora nuestra configuración predeterminada de webpack:

next.config.js
module.exports = {
  webpack(config, { dev }) {
    // ¡modifícalo!
    return config;
  },
};

Un ejemplo del archivo opcional next.config.js

Sin embargo, Webpack solo se ejecutaría en los paquetes del cliente (navegador), y te perderías la posibilidad de usar esta gran cadena de herramientas para el renderizado del servidor.

Nos complace anunciar que hemos refactorizado extensamente nuestra base de código para hacer que Webpack funcione universalmente.

Desde tu perspectiva, todo lo que cambia es que se pasa una propiedad adicional isServer a la función decoradora anterior. Sin embargo, la nueva semántica significa que el amplio ecosistema de cargadores de Webpack ahora está disponible para que lo uses.

CSS, LESS, SASS, SCSS y Módulos CSS

Una de nuestras características más solicitadas es la capacidad de importar archivos CSS y aprovechar los cargadores de Webpack:

import './index.css';
 
export default function Index() {
  return (
    <div>
      <p>¡Amo CSS!</p>
    </div>
  );
}

Una página de ejemplo (pages/index.js) que usa importaciones CSS gracias a Webpack Universal

Para que esto funcione, puedes traer los cargadores que necesitas como dependencias peer:

Terminal
npm i --save css-loader style-loader postcss-loader

Next.js te da la libertad de elegir qué cargadores necesitas y actualizarlos a diferentes versiones a voluntad.

Y luego extender la configuración para configurar tus cargadores. En next.config.js:

next.config.js
module.exports = {
  webpack(config, options) {
    const { dev, isServer } = options;
    const extractCSSPlugin = new ExtractTextPlugin({
      filename: 'static/style.css',
      disable: dev,
    });
    config.module.rules.push({
      test: /\\.css$/,
      use: cssLoaderConfig(extractCSSPlugin, {
        cssModules,
        dev,
        isServer,
      }),
    });
    return config;
  },
};

Extender la configuración cruda de webpack te da gran flexibilidad y control

Si bien nuestra recomendación general es usar soluciones de estilo locales a componentes, como el plugin babel styled-jsx incluido, creemos que los cargadores CSS tienen muchas fortalezas importantes, como facilitar la reutilización de bases de código CSS existentes y simplificar enormemente la migración de bases de código antiguas a Next.js.

En lugar de habilitar cada característica y cargador concebible por defecto, estamos introduciendo Plugins de Next.js, que son funciones simples que decoran tu configuración. En lugar de extender manualmente la configuración para configurar los cargadores como hicimos arriba, puedes simplemente hacer:

const withCss = require('next-css');
module.exports = withCss({
  /* configuración extra opcional */
});

Todo lo que se necesita para habilitar la importación de archivos .css es traer next-css

Lee más sobre el uso de Cargadores CSS con Next.JS, o consulta algunos de los paquetes que ya hemos creado para ti:

CargadorPaquete
CSSnext-css
LESSnext-less
SASSnext-sass

Nuestro objetivo es capacitar a la comunidad para desarrollar y hacer crecer un ecosistema de extensiones prácticas y simples. Para ello, estamos abriendo el monorepo next-plugins para que la comunidad de Next.js lo mantenga. ¡Todas las PR son bienvenidas!

Soporte para TypeScript

Una de las tecnologías de más rápido crecimiento en el ecosistema de JavaScript es TypeScript. Tanto es así, que está siendo oficialmente soportado por Babel 7, lo que significa que naturalmente será soportado por Next.js simplemente personalizando tu .babelrc.

Mientras tanto, gracias a nuestro nuevo soporte de Webpack Universal, ¡ya puedes obtener soporte completo para TypeScript hoy!

Puedes extender tu configuración de webpack así:

next.config.js
module.exports = {
  webpack(config, options) {
    const { dir, defaultLoaders } = options;
    config.resolve.extensions.push('.ts', '.tsx');
    config.module.rules.push({
      test: /\\.+(ts|tsx)$/,
      include: [dir],
      exclude: /node_modules/,
      use: [
        defaultLoaders.babel,
        { loader: 'ts-loader', options: { transpileOnly: true } },
      ],
    });
    return config;
  },
};

Todo lo que tenemos que hacer es habilitar el ts-loader

Al igual que los cargadores CSS y los preprocesadores, TypeScript ha sido una de las características más solicitadas. Para incorporarlo a los proyectos tan fácilmente como cualquier otro cargador, ahora tenemos un plugin next-typescript que puedes incluir en tu archivo next.config.js:

next.config.js
const withTs = require('next-typescript');
module.exports = withTs({
  /* configuración adicional */
});

Los plugins pueden componerse trivialmente: son solo funciones

Mejor Soporte para Alternativas a React y Sobrecarga de Módulos

Con el tiempo han surgido muchas implementaciones alternativas de React. Entre ellas, algunas notables son [preact](https://preactjs.com/), nervjs y inferno.

Otras bibliotecas se centran en reemplazar el renderizador DOM, como react-dom-lite, que tiene como objetivo hacer una compilación más pequeña de React introduciendo algunas compensaciones menores en la compatibilidad del navegador.

El soporte de Webpack Universal hace que el proceso de incorporar estas bibliotecas como reemplazos directos sea aún más fácil. En la misma línea que los otros plugins, esto es todo lo que tienes que hacer para usar Next.js con preact:

Terminal
npm i @zeit/next-preact preact preact-compat

Instalamos el plugin de preact y las dependencias peer necesarias

const withPreact = require('@zeit/next-preact');
module.exports = withPreact();

Nuestro nuevo next.config.js listo para preact

Echa un vistazo al muy simple módulo @zeit/next-preact o ¡crea el tuyo propio!

Mapas de Fuente Externos Opcionales en Producción

Ahora que Next.js utiliza webpack tanto para el código del cliente como del servidor, habilitar los mapas de fuente (source maps) en las compilaciones de producción es solo un pequeño ajuste en su configuración.

En desarrollo, los mapas de fuente se habilitan automáticamente, por lo que los configuramos de manera diferente para producción:

next.config.js
module.exports = {
  webpack(config, { dev }) {
    if (!dev) {
      config.devtool = 'source-map';
    }
    return config;
  },
};

Simplemente configuramos la opción devtool de manera diferente cuando no estamos en desarrollo

Zonas

Uno de los objetivos declarados de Next.js desde el principio fue recuperar y preservar la simplicidad de la Web.

El renderizado del lado del servidor (SSR), un enfoque simple y agnóstico para la obtención de datos y las páginas declarativas basadas en la estructura del sistema de archivos son algunas de las características que introdujimos en línea con este pensamiento.

Un aspecto frecuentemente pasado por alto de los servicios web y los sitios web es lo naturalmente componibles y escalables que son.

Por ejemplo, mydomain.com/settings y mydomain.com/ podrían ser dos aplicaciones completamente diferentes, implementadas de forma independiente, escaladas de forma independiente, incluso ejecutando diferentes versiones del mismo software.

Todo lo que se necesita para "unirlas" en una experiencia uniforme para el usuario final es alguna configuración simple de la capa de enrutamiento del backend o los balanceadores de carga que las exponen al mundo. Estamos muy contentos de ahora poder componer múltiples aplicaciones construidas con Next.js, conectadas entre sí usando componentes normales <Link>**. Llamamos a esta característica Zonas.

Como ejemplo, considere estas dos aplicaciones independientes de Next.js implementadas en Vercel:

Ambas páginas tienen una experiencia perfecta, pero pertenecen a aplicaciones separadas

Ambas páginas tienen una experiencia perfecta, pero pertenecen a aplicaciones separadas

Cuando renovamos nuestra documentación, queríamos que aceptar una contribución de la comunidad fuera lo más fácil posible.

Decidimos separar el "mini-sitio web" de documentación en su propio repositorio. Además, cada vez que se envía una solicitud de extracción (pull request) y se propone un cambio, lo implementamos automáticamente, de forma aislada:

Cada vez que ocurre un cambio dentro de un PR, nuestro bot lo implementa automáticamente

Cada vez que ocurre un cambio dentro de un PR, nuestro bot lo implementa automáticamente

Lo que terminamos teniendo son dos zonas, que se unen en el dominio principal https://vercel.com usando nuestra función de alias de ruta. Se ve algo así:

{
  "rules": [
    { "pathname": "/docs", "dest": "our-docs.vercel.app" },
    { "pathname": "/api", "dest": "our-docs.vercel.app" },
    { "dest": "my-main-website.vercel.app" }
  ]
}

Estas reglas simples le permiten componer microservicios y zonas juntos

Todo lo que queda es invocar un comando now alias:

Terminal
now alias -r rules.json my-domain.com

Nuestra misión es hacer que la implementación sea lo más universal y abierta posible. Para ayudar con el desarrollo local, recientemente hicimos open source de micro-proxy, una herramienta que funciona con el mismo formato de configuración visto arriba.

De manera similar, puede unir zonas con otras soluciones como Nginx, HAProxy o API Gateway.

Tiempos de Compilación en Producción Más Rápidos

Creemos que la experiencia del desarrollador y la experiencia del usuario van de la mano. Cuanto más eficientemente se puedan escribir, probar e implementar los cambios, más rápido se agregarán nuevas funciones, se solucionarán errores y mejorará la experiencia general del usuario.

En consecuencia, seguimos enfocados en iterar continuamente sobre el perfil de rendimiento de los bloques de construcción más básicos del sistema.

Con Next.js 5.0 tuvimos la oportunidad de revisar nuevamente next build, el comando que ejecuta antes de implementar en producción o exportar su aplicación Next.js como un sitio estático.

Nos complace informar que para vercel.com, una aplicación React compuesta por miles de componentes, hemos visto mejoras bastante dramáticas con Next.js 5.0, del orden de 23.6% más rápido en tiempos de compilación en producción:

Nuestra compilación de producción principal ahora tarda 38 segundos menos en completarse

Nuestra compilación de producción principal ahora tarda 38 segundos menos en completarse

Mejor Caché para Importaciones Dinámicas

Cada vez que utiliza import() dinámico, esto indica a WebPack que existe un nuevo punto de entrada para la división de código (code-splitting).

En tiempo de compilación, esto significa generar un paquete específico para el subárbol correspondiente de módulos.

Antes de Next.js 5.0, los paquetes dinámicos recibían una URL similar a la siguiente:

/_next/1517592683901/webpack/chunks/components_hello1_1345d10fc951cd6717c5676c467579a6.js

Ahora, hemos convertido las importaciones dinámicas en hashes direccionables por contenido (content-addressable hashes) del contenido del subárbol:

/_next/webpack/chunks/components_hello1_1345d10fc951cd6717c5676c467579a6-b7874680a9e21fb6eb89.js

Esto significa que, entre implementaciones, sus usuarios y clientes no tendrán que volver a descargar innecesariamente el código que ya han utilizado.

Fragmentos

Next.js construye un componente <Document> de nivel superior que se renderiza en el servidor con cada página. Sobrecargar este componente le da control total sobre su marcado, permitiendo muchos casos de uso avanzados.

Parte de ese marcado inicial es la lista de scripts que Next.js necesita evaluar en el lado del cliente. Un _document personalizado se ve así:

pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document';
export default class extends Document {
  render() {
    return (
      <html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

Document le permite personalizar toda la salida renderizada en el servidor de una página

Hasta hace poco, estábamos obligados a envolver nuestros scripts dentro de un <div>.

Con Next.js 5.0, ahora aprovechamos el nuevo soporte para Fragment, lo que se traduce en páginas más ligeras y control total sobre el estilo de la página, sin marcado superfluo.

Errores Más Precisos

Node.js no admite mapas de fuente, los errores que ocurren en el lado del servidor van acompañados de un seguimiento de pila (stack trace) que apunta al código compilado.

Con Next 5 hemos mejorado el soporte de mapas de fuente en el lado del servidor. Los errores que ocurren al renderizar en el servidor ahora apuntan a la función y número de línea correctos.

Los errores ahora muestran la línea, archivo y nombre de función correctos

Los errores ahora muestran la línea, archivo y nombre de función correctos

Conclusión

Webpack universal consolida los cimientos de Next.js y lo hace aún más preparado para el futuro. En general, ya no existe una separación artificial sobre qué complementos o cargadores son aplicables a Next.js y cuáles no.

En el espíritu de cero configuración, nos complace presentar Next Plugins, el repositorio de la comunidad para recetas que expanden automáticamente la funcionalidad de Next.js, sin tener que ajustar configuraciones específicas.

Con esto, ahora admitimos todo el espectro de soluciones CSS, lenguajes compilados a JS como TypeScript y alternativas a React como Nerve, simplemente agregando un módulo adicional y siendo explícito sobre su inclusión en next.config.js. Simplicidad sin oscuridad.

Las zonas permiten interconectar aplicaciones Next.js que no están enraizadas en el mismo repositorio o incluso servidor(es). Consideramos esto un hito muy importante en la categoría de mejoras de "escalabilidad de equipo".

Next.js se convierte así en un gran candidato para aplicaciones grandes mantenidas por múltiples equipos. Ahora pueden implementar mejoras de manera concurrente, reduciendo superficies de error, aumentando la velocidad de iteración e incluso probando diferentes tecnologías además de nuestro núcleo, como los muchos enfoques diferentes para la gestión de estado o la obtención de datos.

Queremos aprovechar esta oportunidad para agradecer a Deep Varma y al equipo de ingeniería de Trulia por contribuir con ideas clave, código y pruebas que llevaron al diseño de esta característica.

Como siempre, este lanzamiento no habría sido posible sin los muchos contribuidores de código abierto y nuestra maravillosa comunidad.