Carga diferencial en Angular 8

Marzo 25, 2021

Introducción

El tamaño y la complejidad de las aplicaciones web crecen constantemente cada año. Estamos construyendo herramientas sofisticadas, que requieren que se envíen más bibliotecas de JavaScript a los navegadores de los clientes. En 2020, la cantidad media de JavaScript incluida en una página coronado 440 KB. Esta tendencia se suma continuamente al peso total de la página y está afectando su rendimiento, tanto el tiempo de carga como el tiempo de ejecución.

Además, el entorno de ejecución de JavaScript cambia constantemente. Los navegadores están enviando nuevas API, agregando soporte para la nueva sintaxis de JavaScript, y están surgiendo nuevos estándares. Como desarrollador, le gustaría aprovecharlos para mejorar la experiencia del usuario, pero ¿cómo hacerlo si necesita que el sitio web funcione bien también en navegadores heredados?

La respuesta ha sido tradicionalmente relleno de polietileno y transpilacion. Abordaremos brevemente estos enfoques más adelante en este artículo. El problema, sin embargo, es que estos enfoques agregan aún más JavaScript a la aplicación.

En este artículo, explicamos por qué y cómo se utiliza la carga diferencial para admitir navegadores heredados sin sobrecargar el JavaScript enviado a los navegadores modernos.

Comprender la carga diferencial en Angular 8 y cómo configurarla manualmente.

¿Para qué se utiliza la carga diferencial?

La carga diferencial garantiza que los navegadores más modernos no se llenen de polyfills innecesarios y código transpilado dirigido a navegadores heredados, mientras que los navegadores heredados reciben el paquete completo para garantizar la compatibilidad.

La carga diferencial también resuelve la necesidad de una transpilación innecesaria hasta ECMAScript 5 y la inclusión de polyfills redundantes para los navegadores modernos que no la requieren.

Evolución de las API del navegador y el lenguaje JavaScript

JavaScript y los navegadores han recorrido un largo camino y el ecosistema cambia constantemente. Echemos un vistazo a las principales dimensiones de este cambio, que son los principales impulsores de la técnica en cuestión.

Evolución y transpilación de la sintaxis de JavaScript

JavaScript es una implementación de ECMAScript especificación así como Node.js. En el pasado, ECMAScript los lanzamientos de versiones fueron poco frecuentes, hasta el punto de que tuvimos que esperar años para el lanzamiento de ES6. El organismo que rige este proceso es TC39 y, afortunadamente, adoptaron un enfoque más incremental, lo que resulta en lanzamientos de versiones mucho más frecuentes.

Algunas de las características más notables introducidas por ES6 fueron clases, literales de plantilla, funciones de flecha y muchas más. Puedes referirte a esto tabla de compatibilidad para referencia y más detalles sobre el alcance ES6. Todas estas funciones están ampliamente disponibles hoy en día en los navegadores modernos. Desde entonces, hemos tenido varios lanzamientos anuales, incluidas más mejoras en el lenguaje como async / await o recientemente el operador de encadenamiento opcional.

Un ejemplo de la función async / await.
Asincronización / espera de nivel superior con importaciones dinámicas.

Las nuevas funciones de idioma pasan por múltiples etapas de aprobación, que se pueden monitorear en el TC39 oficial Perfil de GitHub.

Imagina que quisieras aprovechar las construcciones del lenguaje moderno. Si no fuera por la transpilación, tendría que esperar años para que los navegadores lo admitieran por completo. En su lugar, podemos transpilar la sintaxis del lenguaje moderno en construcciones disponibles para navegadores heredados y, por lo tanto, comenzar a aprovechar instantáneamente nuevas capacidades en nuestros proyectos.


Note: Hay que tener en cuenta que no todas las construcciones del lenguaje pueden transpilarse.


Evolución de las API del navegador y Polyfills

El organismo que rige la evolución de las API de los navegadores es W3C. Uno puede referirse a lo mismo matriz como para ECMAScript para ver qué nuevos métodos o propiedades se agregan a las clases proporcionadas globalmente en los navegadores. Además, hay una gama completamente nueva de funciones, como los trabajadores del servicio, API de WebSocketo MutaciónObservador.

Algunos de ellos pueden tener relleno de polietileno. Un polyfill es una biblioteca de JavaScript que agrega o parchea una funcionalidad en navegadores heredados. Por ejemplo, MutaciónObservador se puede rellenar con polietileno, mientras que WebSocket conexión o Trabajador de Servicios no poder.

Gestión de dependencias de JavaScript

Durante mucho tiempo, JavaScript no tuvo ningún mecanismo de manejo de dependencias. Uno tendría que asegurarse manualmente de que todas las dependencias requeridas para su sitio web o aplicación web se incluyeron y ordenaron correctamente.

Esto se estaba convirtiendo rápidamente en una pesadilla de mantenimiento a medida que aumentaba la complejidad y la cantidad de dependencias. Ese enfoque claramente no era escalable y se propusieron varios enfoques para llenar este vacío antes de que surgiera el estándar oficial.

RequireJS

Un enfoque de carga de módulo es el Definición de módulo asíncrono (AMD). RequireJS es un ejemplo de implementación de esta especificación. Es un primer intento de resolver el problema de la falta de funcionalidad de manejo de dependencias en los navegadores.

Su naturaleza asincrónica significa que el código que requiere dependencias se ejecuta como parte de una función de devolución de llamada que se invoca solo después de que dichas dependencias estén disponibles.

Un ejemplo de la estructura de sintaxis de AMD.
Ejemplo de sintaxis de AMD

CommonJS

CommonJS fue desarrollado principalmente para Node.js y, a diferencia de AMD, es un mecanismo de carga de módulo síncrono. Es posible aprovecharlo en el lado del navegador utilizando herramientas de compilación como Navegar or paquete web.

Un ejemplo de la sintaxis de CommonJS.
Ejemplo de sintaxis CommonJS

Definición de módulo universal

Había una mezcla en el ecosistema, con algunos paquetes que aprovechaban la Definición de módulo asíncrono y algunos el Módulo síncrono mecanismo de carga. Por lo tanto, la Definición de módulo universal (UMD) fue creado.

Según su nombre, la definición de módulo universal está destinada a ser una sintaxis universal, que funciona tanto en el lado del servidor como en el del cliente. Detecta en qué configuración se ejecuta (AMD or CommonJS) y ejecuta el manejo de módulos en consecuencia.

Un ejemplo de sintaxis UMD.
Definición de UMD recuperada de umdjs / umd

Módulos ECMAScript

Todo esto nos llevó a los Módulos ECMAScript (ESM), una forma estandarizada a nivel de lenguaje de manejar módulos, disponible tanto en Node.js como en el lado del cliente en los navegadores modernos, y aprovechada en la carga diferencial.

Un ejemplo de la sintaxis de ESM.
Ejemplo de sintaxis de ESM

Transpilacion

La transpilación es el proceso de compilación de código fuente JavaScript basado en versiones más antiguas. ECMAScript especificación.

Ejemplo de transición a ES5.
Sintaxis de clase moderna transpilada a ES5

En el ejemplo anterior, puede ver ES6 clase definición utilizando sintaxis moderna y, a la derecha, la versión transpilada que aprovecha un cierre y un constructor de funciones.

Hay una serie de herramientas para manejar este proceso, las dos más populares son Mecanografiado y Babel. La transpilación suele ser un paso de compilación en su aplicación. En caso de carga diferencial, el resultado serían dos versiones del código base de JavaScript: una transpilada para el uso de navegadores heredados y una original destinada a navegadores modernos.

polirellenos

Por otro lado, los polyfills están destinados a proporcionar una implementación o un parche para una API de navegador estándar que aún no está disponible, por ejemplo Objeto.claves or Cadena.prototipo.padInicio.

núcleo-js es un ejemplo de un conjunto popular de polyfills, siendo casi una ventanilla única para admitir API recientes en navegadores heredados.

El denominador común de las funcionalidades que se pueden polirrellenar es la posibilidad de imitar dicha funcionalidad con APIs existentes y características del lenguaje JavaScript.

¿Cómo funciona la carga diferencial?

La base para la carga diferencial es una nueva módulo tipo para HTML guión etiquetas, así como también el nuevo nomódulo atributo disponible para etiquetas de script. El concepto es bastante sencillo y es el siguiente:

Los navegadores modernos reconocen ambos módulo mecanografiado y nomódulo atributo, por lo tanto, están cargando y procesando el módulo solamente.Los navegadores heredados no saben cómo manejar módulo tipo guión etiquetas y, por lo tanto, ignorándolas por completo, mientras se usa la marcada con el atributo nomódulo como es el estándar text / javascript tipo.
Carga diferencial con dos versiones de JavaScript de la aplicación.
Carga diferencial usando dos versiones del JavaScript de la aplicación

En el fragmento anterior, puede ver que Chrome descarga y procesa esm.paquete.mjs archivo ya que se incluye utilizando el guión etiqueta de tipo módulo. Chrome ignora el es5.paquete.js ya que el guión la etiqueta está marcada como nomódulo. IE9, por otro lado, hace lo contrario, ya que no es capaz de procesar guión etiquetas de tipo módulo, y el otro se percibe como un habitual guión etiqueta.

Si desea leer más sobre la naturaleza de Módulos ECMAScript puedes referirte a esto redacción sucinta.

Carga diferencial en angular

Carga diferencial es manejado por el Cliente angular automáticamente, basado en el lista de navegadores objetivo para su aplicación. A partir de Angular 10, asume que uno está apuntando solo al subconjunto moderno de navegadores, deshabilitando así la compilación de ES5 y la carga diferencial por completo. Sin embargo, tras la actualización de la lista de navegadores compatibles, este comportamiento se modifica según la lista proporcionada.

Dos conjuntos de paquetes proporcionados por Angular Client.
Dos conjuntos de paquetes de aplicaciones proporcionados por Angular Client

Como se ve en el fragmento anterior, Cliente angular generó dos conjuntos de paquetes para nuestra aplicación como se requiere para carga diferencial - un conjunto de recursos ES2015 + y ES5. También enmendó el index.html archivo en segundo plano con las etiquetas de secuencia de comandos necesarias para cargar y arrancar la aplicación.

Al observar los archivos generados, el ahorro en términos de tamaño de carga útil es claramente visible. Por ejemplo, común-es5 el paquete (navegadores heredados) pesa 15 KB, mientras paquete common-es2015 (navegadores modernos) pesa 13.1KB. La diferencia es aún más significativa cuando observamos polyfills: ES5 (navegadores heredados) es casi 70KBy ES2015 (navegadores modernos) es solo Bytes 130!

Cómo configurar manualmente la carga diferencial

tener carga diferencial configurado, uno tiene que tener una compilación que genere dos tipos de paquetes distintos. Una compilación que genera un paquete ES5 y otra que genera el moderno, ES2015 +.

Es muy fácil cambiar entre destinos de compilación en ambos Babel y Mecanografiado. Es suficiente ejecutar su transpilador a través de un paquete de su elección (es decir, paquete web, enrollaro paquete) dos veces, proporcionando un archivo de configuración de transpilador diferente para que lo asimile. Para obtener más detalles, consulte el archivo de configuración .babelrc de ejemplo a continuación:

{
    "presets": [
        ["env", {
            "targets": {
                "browsers": ["last 2 versions"] // target determined from browsers list
            }
        }]
    ],
    "plugins": ["external-helpers"]
}

Y este ejemplo de archivo de configuración de TypeScript:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "module": "esnext",
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015", // target ES version
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  }
}

Todos esos pasos se pueden componer en un solo comando usando npm or hilo scripts.

{
  "name": "the-portal",
  "version": "0.0.1",
  "scripts": {
     "build:diff": "yarn webpack:es5 && yarn webpack:es2015",
     "webpack:es5": "webpack --config config/webpack.es5.js --mode production",
     "webpack:es2015": "webpack --config config/webpack.es2015.js --mode production"
  },
 …
}

El ejemplo anterior invoca la construir: diff script, que a su vez llamaría paquete web dos veces para producir dos versiones de la aplicación, de acuerdo con los archivos de configuración que se alimentó. Una vez hecho esto, todo lo que tiene que hacer es incluir los paquetes producidos en su index.html archivo utilizando la técnica descrita anteriormente.

Conclusión

Carga diferencial es una optimización de rendimiento interesante que se puede aplicar en todas las aplicaciones web. No solo produce una reducción en el peso de la aplicación y, por lo tanto, reduce el tiempo de carga inicial, sino que también limita la cantidad de procesamiento que el navegador tiene que hacer en JavaScript cargado.

La carga diferencial permite posibles mejoras en el rendimiento del tiempo de ejecución en los navegadores modernos gracias al hecho de que aprovechan las implementaciones nativas de las API que, de otro modo, se habrían rellenado con JavaScript personalizado más lento. El único precio que se debe pagar es una configuración de compilación un poco más compleja y la compilación de aplicaciones tarda más.

La carga diferencial definitivamente vale la pena considerarla, ya que la configuración no es complicada y, en algunos casos, como para Angular, está disponible de inmediato. El concepto es fácil de comprender y, por lo tanto, no agrega mucho a la complejidad general de la configuración, brindando retornos medibles en forma de mejoras de rendimiento y UX.

Configurarlo para su aplicación no causará ningún daño, pero las ganancias de él dependen en gran medida de la lista de navegadores que su aplicación debe admitir, así como de las API y la sintaxis de JavaScript utilizada en su base de código. La carga diferencial siempre es beneficiosa, pero produce mayores ahorros cuando la aplicación aprovecha las últimas funciones del lenguaje JavaScript y tiene un amplio espectro de navegadores para admitir.

Acerca del autor.
Artur Bañas
Hable con un especialista en asistencia comercial