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';
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) {
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?
