Módulos ES6 en el navegador: ¿están listos o no?

¿Has oído hablar del uso de módulos ES6 en un navegador? En realidad, estos son módulos ES6 ordinarios. Solo se aplican en el código destinado a los navegadores.



Si alguien no lo sabe, así es como se ve.

Hay una página index.html:

<!DOCTYPE html>
<html>
  <body>
    <div id="app"></div>
  
    <script type="module" src="index.mjs"></script>
    <!--     "module" -->
  </body>
</html>

Hay un archivo que index.mjsrepresenta un módulo conectado a la página:

import { doSomething } from './utils.mjs';

doSomething('Make me a sandwich');

Este módulo, a su vez, importa la función doSomethingdel módulo utils.mjs:

export const doSomething = message => {
  console.log('No.');
};

Los módulos ES6 han existido durante muchos años. Y tal vez estás pensando en probarlos. Los usé durante aproximadamente un mes en mi propio proyecto. Al mismo tiempo, llegué a la conclusión de que ...

¡Suspensión!

Acerca del soporte del navegador para módulos


Antes de hablar sobre lo que entiendo, sugiero echar un vistazo al soporte del navegador para módulos. Estoy escribiendo este material a principios de 2020, por lo que el nivel de soporte para los módulos es bastante impresionante del 90%.


Soporte para módulos con navegadores de acuerdo con caniuse.com

Estoy bastante satisfecho con este 90% (no tengo en cuenta las necesidades del 10% de los usuarios), aunque es posible que desee ser más responsable. Pero, incluso teniendo en cuenta esto, si su proyecto no está diseñado para IE, UC u Opera Mini, este nivel de soporte significa que casi el 100% de su público objetivo podrá trabajar con su proyecto sin problemas.

Es cierto que el soporte del navegador para módulos es solo el comienzo. Aquí quiero encontrar la respuesta a tres preguntas que surgieron al comienzo de mi viaje hacia los módulos:

  • ¿El uso de módulos en los navegadores tiene alguna ventaja?
  • ¿Qué pasa con los contras?
  • Estoy seguro de que tenía una tercera pregunta, pero ahora no la recuerdo.

Vamos a resolverlo ...

¿Cuáles son las ventajas de usar módulos en los navegadores?


¡Esto es puro JavaScript! Sin proyecto de ensamblaje, sin archivo de configuración Webpack de 400 líneas, sin Babel, sin complementos y ajustes preestablecidos, y sin módulos adicionales de 45 npm. Solo tú y tu código.

Hay algo refrescante en escribir código que se ejecutará en el navegador exactamente en la forma en que fue creado. Esto puede requerir un poco más de esfuerzo, pero el resultado es muy agradable. Así es como conducir un automóvil con una transmisión manual.

Entonces. Las ventajas de los módulos ES6 son JavaScript puro, esto es la falta de ensamblaje, la configuración del proyecto y el rechazo de las dependencias que se han vuelto innecesarias. ¿Qué más? ¿Hay algo mas?

No, nada mas.

¿Cuáles son las desventajas de usar módulos en los navegadores?


▍ Comparación de módulos y paquetes


Al pensar en usar módulos ES6 en un navegador, en realidad debe elegir entre usar módulos y paquetes como Webpack, Parcel o Rollup.

Puede (probablemente) usar ambos, pero en realidad, si planea ejecutar el código a través del paquete, no hay razón para usar el diseño <script type="module">para cargar los archivos del paquete.

Entonces, basado en la suposición de que alguien está considerando la posibilidad de usar módulos ES6 en lugar de un paquete, esto es lo que tendrá que renunciar:

  • Minificación del código terminado.
  • Funciones de JavaScript de vanguardia.
  • Sintaxis que es diferente de la sintaxis de JavaScript (React, TypeScript, etc.).
  • Npm módulos.
  • Almacenamiento en caché

Consideremos los primeros tres puntos de esta lista con más detalle.

▍ Tamaño de archivo


El tamaño de mi proyecto, que usa módulos, es de 34 Kb. Como no uso el paso de compilación del proyecto, el código transmitido a través de la red contiene nombres de variables muy largos; hay muchos comentarios en él. Además, representa una gran cantidad de archivos pequeños, lo que no es muy bueno en términos de compresión de datos.

Si pongo todo esto en un paquete usando Parcel , entonces el tamaño de lo que resultaría sería 18 Kb. Mi calculadora Casual Casio informa que esto es "aproximadamente la mitad" del código del proyecto en el que no se usa el paquete. Esto se debe en parte al hecho de que Parcel minimiza los archivos, pero también porque los materiales con este enfoque se comprimen mejor usando gzip.

La compresión de datos llama nuestra atención sobre otro problema: la forma en que se organizan los archivos (en el sentido de conveniencia de desarrollo) se transfiere directamente a la forma en que se transfieren los archivos a través de la red. Y la forma en que se organizan los archivos durante el desarrollo no se corresponde necesariamente con la forma en que desea verlos en el sitio. Por ejemplo, 150 módulos (archivos) pueden tener sentido mientras se trabaja en un proyecto. Y los mismos materiales transferidos al navegador pueden justificarse para organizarse en 12 paquetes.

Explicaré esta idea. No estoy hablando del hecho de que el uso de módulos significa que los archivos no se pueden agrupar y minimizar (el hecho de que esto no se pueda hacer es una ilusión). Solo quiero decir que no tiene sentido hacer ambas cosas.

Sí, aquí hay una observación graciosa. En mi aplicación, hasta que escribí esta sección, se utilizaron módulos (¡enfatizo!). Instalé Parcel para calcular el tamaño de ensamblaje correcto. Y ahora no veo ninguna razón para volver a los módulos normales. Bueno, ¿no es interesante?

▍Vida sin transpilación


A lo largo de los años, estoy muy acostumbrado a usar las últimas construcciones de sintaxis de JavaScript y a la maravillosa herramienta Babel, que las convierte en código que todos los navegadores entienden. Me acostumbré tanto que casi nunca pensé en el soporte del navegador (por supuesto, con la excepción de DOM y CSS).

Cuando lo intenté por primera vez type=«module», iba a hacer todo por mí mismo. Mi objetivo era tener navegadores nuevos, así que pensé que podría usar JavaScript moderno, al que estoy acostumbrado.

Pero la realidad no era tan optimista. Intenta responder las siguientes preguntas rápidamente y sin mirar a ningún lado. ¿Soporta Edge?flatMap()? ¿Safari admite la asignación destructiva (objetos y matrices)? ¿Chrome admite comas después de los últimos argumentos de función? ¿Firefox admite exponenciación?

Por ejemplo, tuve que buscar respuestas a estas preguntas, realizar pruebas de navegador cruzado. Pero usé todo esto por siglos. En cualquier aplicación lo suficientemente grande, esto probablemente conduciría a errores de producción.

Esto también significa que no podré utilizar la innovación JavaScript más notable desde la llegada del método de matriz slice(), el llamado operador de secuencia opcional . Usé diseños mágicos comoprop?.valuecasi un mes (desde el momento en que la herramienta Create React App comenzó a admitirlos sin ninguna configuración adicional). Pero ya es inconveniente para mí trabajar sin ellos.

▍ Cargas de almacenamiento en caché


El almacenamiento en caché fue para mí el mayor obstáculo. De hecho, el hecho de que la solución al problema de almacenamiento en caché resultó ser bastante interesante resultó ser la razón principal por la que decidí escribir este material.

Como está seguro, ya sabe, cuando los materiales se procesan utilizando un paquete, cada uno de los archivos resultantes recibe un nombre único, más o menos index.38fd9e.js. El contenido de un archivo con este nombre nunca (nunca nunca) cambia. Por lo tanto, puede ser almacenado en caché por el navegador por un período ilimitado. Si dicho archivo se descarga una vez, no tendrá que volver a descargarlo.

Este es un sistema sorprendente, a menos que intente encontrar la respuesta a la pregunta de cómo borrar el caché.

Los módulos se cargan usando un diseño como<script type="module" src="index.mjs"></script>. El hash en el nombre del archivo no se usa. ¿Cómo, en esta situación, se supone que debe informar al navegador sobre dónde descargarlo index.mjs, desde la memoria caché o desde la red?

Cabe señalar que es casi posible hacer que el caché funcione aceptable, pero tomará un poco de esfuerzo. Esto es lo que necesitas para esto:

  • Debe establecer el título de todas las respuestas cache-controla un valor no-cache. Increíblemente, sin caché no significa "no almacenar en caché". Esto significa que el archivo debe almacenarse en caché, pero el archivo no debe "usarse para satisfacer una solicitud posterior sin una verificación exitosa en el servidor de origen".
  • ETag. — () , , , . , 38fd9e .
  • CDN- , . ETag . . CDN- . ( ETag) . (, , Firebase).
  • -, , « » . , , , , .

Como resultado, cuando un visitante vuelve a visitar el sitio, el navegador dirá: “Necesito descargar el archivo index.mjs. Veo que este archivo ya existe en mi caché, su ETag es 38fd9e. Solicitaré este archivo al servidor, pero le diré que me lo envíe solo si su ETag no lo es 38fd9e". El trabajador del servicio interceptará esta solicitud, ignorará ETag y la devolverá index.mjsde su caché (este archivo entró en la caché cuando se cargó la página la última vez). Luego, el trabajador del servicio redirigirá la solicitud al servidor. El servidor devolverá un mensaje que indica que el archivo no ha cambiado o un archivo que se almacenará en la memoria caché.

En mi opinión, todo esto es muy problemático.

Ahora, si está interesado, el código de trabajador de servicio:

self.addEventListener('fetch', e => {
  e.respondWith(
    (async () => {
      //       
      const cacheResponse = await caches.match(e.request);
      
      //   (),   
      // (ETag-    )
      fetch(e.request).then(fetchResponse => {
        caches
          .open(CACHE_NAME)
          .then(cache => cache.put(e.request, fetchResponse));
      });
      
      if (cacheResponse) return cacheResponse;
      
      return fetch(e.request);
    })()
  );
});

Era flojo, por lo que no estudié y no utilicé la última propiedad FetchEvent.navigationPreload . El hecho es que para ese momento pasaba más tiempo almacenando en caché que escribiendo una solicitud (para su información, pasé 10 y 11 horas en estos asuntos, respectivamente).

Sí, quiero señalar que la propuesta de la "tarjeta de importación" tiene como objetivo resolver algunos de los problemas anteriores. Le permite organizar algo como mapeo index.jsen index.38fd9e.mjs. Pero para generar un hash, de todos modos, necesita algún tipo de canalización de ensamblaje, las tarjetas de importación deberán estar incrustadas en el archivo HTML. Esto significa que se necesita un paquete aquí ... En realidad, en esta situación, los módulos ya no son necesarios en el navegador.

Como resultado, aunque fue interesante entender todo esto, se puede comparar con la forma en que conduje en un monociclo durante todo un año. Ya no haré esto.

¿Pero probablemente no todos usan paquetes?


Escribí este material asumiendo que todos escriben código en módulos, usando construcciones import/exporto require, y luego recopilan el código en paquetes de producción usando Webpack, Grunt, Gulp o algo así.

¿Hay desarrolladores que no usan paquetes? ¿Hay alguien que ponga su código JavaScript en varios archivos y los envíe a producción sin agruparlos? Tal vez una de estas personas eres tú? Si es así, me gustaría saber todo sobre tu vida.

Resumen


Me esfuerzo por tomar todas las decisiones serias, como elegir entre módulos y paquetes, en base a los principios fundamentales del desarrollo efectivo. Esto es productividad y calidad.

Desafortunadamente, el uso de módulos en los navegadores no contribuye ni a uno ni a otro.

Si mañana empiezo a trabajar en un nuevo proyecto, entonces no tendré una pregunta sobre si ejecutar la buena y antigua aplicación create-react. Todas las configuraciones iniciales tomarán unos treinta segundos, y aunque el tamaño inicial del proyecto de 40 Kb es un poco grande, para la mayoría de los sitios esto no tendrá ningún papel.

Y aquí hay una situación diferente. Supongamos que necesitaría reunir un poco de HTML / CSS y JavaScript para algún tipo de experimento, y ese experimento sería un proyecto que incluía un poco más de archivos que "unos pocos". Si, mientras trabajaba en este proyecto, no planeaba pasar tiempo configurando el sistema para construirlo, entonces probablemente usaría los módulos en el navegador. Y luego, si no me importaba el desempeño de este proyecto.

Me interesaría saber cómo funcionan Webpack y sus herramientas relacionadas con los módulos ES6 en un navegador. Creo que en el futuro, cuando la propuesta de "tarjetas de importación" reciba suficiente apoyo, los paquetes probablemente las usarán como un mecanismo para abstraer hashes antiestéticos que se usan hoy en día.

¡Queridos lectores! ¿Has utilizado módulos ES6 en tu navegador?


All Articles