Uso de módulos JavaScript en producción: estado actual de las cosas. Parte 2

Hoy publicamos la segunda parte de la traducción del material, dedicada al uso de módulos JS en la producción.



→ Por cierto, aquí está la primera parte del artículo.

Importación dinámica


Una de las desventajas de usar expresiones de importación reales para separar el código y cargar módulos es que la tarea de trabajar con navegadores que no admiten módulos recae en el desarrollador.

Y si desea usar import() comandos de import() dinámica import() para organizar la carga de código diferido, entonces, entre otras cosas, tendrá que lidiar con el hecho de que algunos navegadores, aunque, sin duda, admiten módulos , todavía no admiten comandos de importación dinámica () (Edge 16–18, Firefox 60–66, Safari 11, Chrome 61–63).

Afortunadamente, este problema nos ayudará a resolver un polyfill pequeño (de unos 400 bytes de tamaño) y extremadamente rápido.

Agregar este polyfill a un proyecto web es muy simple. Solo necesita importarlo e inicializarlo en el punto de entrada principal de la aplicación (antes de llamar a cualquiera de los comandos import() en el código):

 import dynamicImportPolyfill from 'dynamic-import-polyfill'; //         .   //    ,       . dynamicImportPolyfill.initialize({modulePath: '/modules/'}); 

Y lo último que debe hacerse para que este esquema funcione es decirle a Rollup que necesita cambiar el nombre de los comandos de import() dinámica import() que aparecen en el código usando el nombre que elija (a través de la opción output.dynamicImportFunction ). Un polyfill que implementa la función de importación dinámica usa el nombre __import__ de forma predeterminada, pero se puede personalizar .

La razón por la que necesita cambiar el nombre de las expresiones import() es porque import es, en JavaScript, una palabra clave. Esto significa que es imposible, por medios de polyfill, organizar el reemplazo del comando estándar import() con un comando del mismo nombre. Si intenta hacer esto, se producirá un error de sintaxis.

Pero es muy bueno que Rollup cambie el nombre de los comandos durante la compilación del proyecto, ya que esto significa que puede usar comandos estándar en el código fuente. Además, en el futuro, cuando el polyfill ya no sea necesario, el código fuente del proyecto no tendrá que ser reescrito, cambiando para import lo que anteriormente se llamaba de alguna manera diferente.

Carga eficiente de JavaScript


Siempre que use la separación de código, no está de más organizar la precarga de todos los módulos que sabe que se cargarán muy pronto (por ejemplo, estos son todos los módulos en el árbol de dependencias del módulo principal, que es el punto de entrada al proyecto).

Pero cuando cargamos módulos JavaScript reales (a través de <script type="module"> y luego a través de los comandos de import correspondientes), debemos usar el atributo modulepreload en lugar de la precarga habitual, que está destinada solo para scripts clásicos.

 <link rel="modulepreload" href="/modules/main.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-one.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-two.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-three.XXXX.mjs"> <!-- ... --> <script type="module" src="/modules/main.XXXX.mjs"></script> 

De hecho, modulepreload es definitivamente mejor que el mecanismo tradicional de preload en la precarga de módulos reales. El hecho es que cuando lo usa, no es solo que el archivo se descargue. Además, inmediatamente, fuera del hilo principal, comienza a analizar y compilar el script. Una preload regular no puede hacer esto porque, durante la precarga, no sabe si el archivo se usará como un módulo o como un script regular.

Esto significa que la carga de módulos que usan el atributo modulepreload menudo más rápida, y que cuando los módulos se inicializan, es menos probable que cree una carga excesiva en el hilo principal, causando problemas de interfaz.

Crear una lista de módulos para precargar


El fragmento de entrada en el objeto paquete acumulativo contiene una lista completa de importaciones en sus árboles de dependencia estática. Como resultado, en el enlace Rollup generateBundle , es fácil obtener una lista de archivos que necesitan ser precargados.

Aunque se pueden encontrar complementos en npm para generar listas de carga de módulos, crear una lista similar para cada punto de entrada en el árbol de dependencias requiere solo unas pocas líneas de código. Por lo tanto, prefiero crear tales listas manualmente, usando algo como este código:

 {  generateBundle(options, bundle) {    //         .    const modulepreloadMap = {};    for (const [fileName, chunkInfo] of Object.entries(bundle)) {      if (chunkInfo.isEntry || chunkInfo.isDynamicEntry) {        modulepreloadMap[chunkInfo.name] = [fileName, ...chunkInfo.imports];      }    }    //  -   ...    console.log(modulepreloadMap);  } } 

Aquí, por ejemplo, es cómo creé una lista de módulos de carga para philipwalton.com y para mi aplicación de demostración , de la que hablaremos a continuación.

Tenga en cuenta que aunque el atributo modulepreload definitivamente mejor que la preload clásica para cargar scripts de módulo, tiene el peor soporte para el navegador (actualmente solo Chrome lo admite). Si una parte importante de su tráfico no proviene de Chrome, entonces, en su situación, puede tener sentido utilizar la preload regular en lugar de la preload del preload .

En cuanto al uso de preload , me gustaría advertirte sobre algo. El hecho es que al cargar scripts usando preload , a diferencia de modulepreload , estos scripts no entran en el mapa del módulo del navegador. Esto significa que existe la posibilidad de que las solicitudes de precarga se puedan ejecutar más de una vez (por ejemplo, si el módulo importa el archivo antes de que el navegador haya terminado de precargarlo).

¿Por qué implementar módulos reales en producción?


Si ya usa un paquete como webpack , así como si ya usa la división de código y la carga previa de los archivos correspondientes (similar a lo que acabo de decir), entonces se preguntará si debería cambiar a una estrategia enfocado en el uso de módulos reales. Hay varias razones que me hacen creer que deberías considerar cambiar a módulos. Además, creo que convertir un proyecto en módulos reales es mejor que usar scripts clásicos con su propio código diseñado para cargar módulos.

▍ Reducir la cantidad total de código


Si el proyecto usa módulos reales, los usuarios de navegadores modernos no tendrán que descargar algún código adicional diseñado para cargar módulos o administrar dependencias. Por ejemplo, cuando use módulos reales, no necesitará cargar los mecanismos de tiempo de ejecución y el manifiesto del paquete web .

▍ Código de precarga mejorado


Como se mencionó en la sección anterior, el uso del atributo modulepreload permite cargar código, modulepreload y compilarlo fuera del hilo principal. Todo lo demás, en comparación con el atributo de preload , sigue siendo el mismo. Esto significa que gracias a la modulepreload del modulepreload páginas se vuelven interactivas más rápido, y eso reduce la probabilidad de bloquear la transmisión principal durante la interacción del usuario.

Como resultado, independientemente del tamaño del código de la aplicación se divide en fragmentos, será mucho más productivo descargar estos fragmentos usando los comandos de importación y el atributo modulepreload que cargarlos usando la etiqueta de script habitual y el atributo de preload habitual (especialmente si se generan las etiquetas correspondientes dinámicamente y agregado al DOM en tiempo de ejecución).

En otras palabras, un paquete acumulativo de algún código de proyecto, que consta de 20 fragmentos de módulo, se cargará más rápido que un paquete del mismo proyecto, que consta de 20 fragmentos de script clásicos preparados por webpack (no por el uso de webpack, sino porque que estos no son módulos reales).

▍ Mejorando el enfoque futuro del código


Muchas características nuevas y excelentes de los navegadores se basan en módulos y no en scripts clásicos. Esto significa que si planea utilizar estas funciones, su código debe presentarse en forma de módulos reales. No debería ser algo transpuesto en ES5 y cargado con los medios de la etiqueta de script clásica (este es el problema sobre el que escribí cuando traté de usar la API experimental de almacenamiento KV ).

Estas son algunas de las nuevas características más interesantes del navegador que se centran exclusivamente en los módulos:


Soporte de navegador heredado


A nivel mundial, más del 83% de los navegadores admiten módulos JavaScript (incluida la importación dinámica), como resultado, la mayoría de los usuarios podrán trabajar con un proyecto que cambió a módulos sin ningún esfuerzo especial por parte de los desarrolladores de este proyecto.

En el caso de los navegadores que admiten módulos pero no admiten la importación dinámica, se recomienda utilizar el polyfill dinámico de importación dinámica polyfill descrito anteriormente. Dado que es muy pequeño y, si es posible, utiliza el método estándar import() basado en el navegador, el uso de este polyfill casi no tiene efecto en el tamaño o el rendimiento del proyecto.

Si hablamos de navegadores que no admiten módulos, entonces, para organizar el trabajo con ellos, puede usar el patrón de módulo / módulo .

▍ Ejemplo operativo


Como siempre es más fácil hablar sobre la compatibilidad entre navegadores que lograrlo, creé una aplicación de demostración que utiliza las tecnologías mencionadas anteriormente.

Esta aplicación funciona en navegadores como Edge 18 y Firefox ESR, que no admiten comandos de import() dinámica import() . Además, funciona en navegadores como Internet Explorer 11, que no admiten módulos.

Con el fin de mostrar que la estrategia discutida aquí es adecuada no solo para proyectos simples, utilicé muchas funciones en esta aplicación que se necesitan hoy en proyectos grandes:

  • Transformación de código usando Babel (incluido JSX).
  • Dependencias de CommonJS (por ejemplo, react y react-dom).
  • Dependencias CSS.
  • Hashing de recursos
  • Separación de código
  • Importación dinámica (con un respaldo como polyfill).
  • Implementación del patrón de módulo / nomódulo.

El código del proyecto se puede encontrar en GitHub (es decir, puede bifurcar el repositorio y construir el proyecto usted mismo), la versión demo está alojada en Glitch , lo que le permite experimentar con él.

Lo más importante en el proyecto de demostración es la configuración de Rollup , ya que determina cómo se crean los módulos resultantes.

Resumen


Espero que este material lo haya convencido no solo de la posibilidad de implementar módulos JavaScript estándar en producción, sino también de que realmente puede mejorar el tiempo de carga del sitio y su rendimiento.

Aquí hay una breve descripción de los pasos que debe seguir para implementar los módulos en el proyecto:

  • Utilice un paquete, entre los formatos de salida admitidos por los cuales hay módulos ES2015.
  • Enfoque agresivo de separación de código (si es posible, hasta la asignación de dependencias de node_modules en fragmentos separados).
  • Precargue todos los módulos que están en su árbol de dependencia estática (usando modulepreload ).
  • Use polyfill para navegadores que no admiten declaraciones dinámicas de import() .
  • Utilice el patrón de módulo / módulo para organizar el trabajo con navegadores que no admiten módulos.

Si ya está utilizando Rollup para construir el proyecto, me gustaría que pruebe lo que estaba hablando aquí y que vaya a implementar módulos reales en producción (usando la separación de código y técnicas de importación dinámica). Si lo haces, avísame cómo te va. Estoy interesado en conocer problemas y casos exitosos de introducción de módulos.

Está muy claro que los módulos son el futuro de JavaScript. Me gustaría ver, y preferiblemente pronto, cómo las herramientas que utilizamos y las bibliotecas auxiliares adoptan esta tecnología. Espero que este material al menos pueda ayudar un poco en este proceso.

Estimados lectores! ¿Usas módulos JS en producción?

Source: https://habr.com/ru/post/466539/


All Articles