Lazy load de componentes en Polymer

  • Por
Lazy load en Polymer, la carga perezosa de componentes para aligerar la primera visualización de las apps y descargar solo los web components necesarios para comenzar.

El lazy load de componentes en Polymer es sorprendentemente sencillo. En dos líneas de código lo tienes resuelto, aunque en el marco de una aplicación habrás de configurar diversos aspectos que vamos a tratar en este artículo.

Básicamente la carga perezosa (lazy load) consiste en descargar solamente el código de tus componentes cuando realmente sea necesario, evitando que se necesite cargar todo el código de la aplicación en el primer acceso. Es algo que tiene mucho sentido, pues para poder visualizar la primera pantalla de una aplicación realmente no necesitamos cargar el código de todas las pantallas, con todos sus componentes. Seguramente hay muchas secciones de la aplicación que no se requiere tener todavía, incluso que en una visita de un usuario a tu Progressive Web App ni siquiera se lleguen a usar.

El Polymer Starter Kit ya viene preparado para realizar la carga perezosa, es decir, ya está configurado para disponer de varias pantallas con componentes que realmente no se descargarán hasta que se necesiten, por lo que la mayor parte del trabajo de configuración ya lo tienes hecho, y tendrás que usar ese mismo esquema para ir implementando nuevas secciones de tu aplicación. De todos modos, lo vamos a explicar bien para que lo puedas reconocer y usar fácilmente, o incluso que puedas generar lazy load de componentes por tu cuenta, sin apoyarte en el Polymer Starter Kit.

No necesitas hacer los import de todos tus componentes

En general, cada componente tiene que hacer los import de sus dependencias (aquellos componentes que está usando). Sin embargo nos saltaremos la regla con los componentes que queramos cargar de manera perezosa. El primer paso para conseguir lazy load, por tanto, consiste en no colocar los HTML import de todos tus componentes. Simplemente, en el componente raíz de tu aplicación no vas a colocar los import de los web components que no se necesitan para la primera visualización de tu app, en la Home.

Todos esos componentes no se cargarán y, aunque aparezcan dentro del código de tu página, el navegador no va a saber qué hacen exactamente. Para él serían como etiquetas desconocidas.

Los componentes que son candidatos a hacer lazy load son los que formen parte de rutas internas de tu aplicación.

Nota: Para las presentes explicaciones vamos a basarnos en el conocimiento previo, que hemos ofrecido en los primeros artículos dedicados al sistema de routing de Polymer. En la anterior entrega vimos cómo crear rutas de la aplicación y cómo intercambiar vistas para mostrar una u otra pantalla según sea la ruta. Esos componentes que se muestran según la ruta sea una u otra son los candidatos a implementar con lazy load a los que nos referimos.

Para que nos quede claro, yo puedo tener varias vistas que se intercambian en un iron-pages. Cada vista está creada en un componente independiente.

<iron-pages
  selected="[[data.page]]"
  attr-for-selected="page"
>
  <productos-page page="productos"></productos-page>
  <mi-cuenta-page page="mi-cuenta"></mi-cuenta-page>
  <contacto-page page="contacto"></contacto-page>
</iron-pages>

Recuerda que "data.page" (bindeado al atributo selected del iron-pages) era la página actual que quieres ver, que te entrega el componente app-route.

Cómo cargar los componentes bajo demanda

Hemos quedado entonces que los componentes pueden estar usándose en el HTML, pero no necesariamente el navegador los debe conocer. Entonces ¿cómo detectar cuándo se necesitan esos componentes? y ¿cómo cargarlos en ese momento?

El secreto está en un par de métodos de instancia de todos los componentes de Polymer, que permiten traerse el contenido de una URL. Esa URL será el código de tu componente que no habías descargado antes.

El momento en el que realizar la descarga es aquel en el que detectes que tu ruta ha cambiado. Ya sabes que los observers de Polymer son el sitio apropiado para observar cambios de propiedades de tu aplicación, por lo que vamos a definir uno.

observers: [
   'routeChanged(data.page)'
],

El método "routeChanged" es el que se encargará de cargar los componentes en el momento apropiado.

routeChanged: function(page) { if(page) { this.importHref( this.resolveUrl('pages/' + page + '-page.html'), null, null, true); } }

Primero pregunto si tengo algo en "page", porque si no, es que estoy viendo la raíz de la aplicación y no necesito cargar nada.

Luego uso el método de instancia "importHref", que es el encargado de importar un contenido, con el código del componente que necesite. La URL a importar la compongo a mano y uso el método "resolveUrl" para que me de una ruta relativa al componente actual. Mira la documentación de Polymer para saber más sobre estos métodos útiles.

Eso es todo lo que tenemos que hacer. A mi me parece asombroso! A nivel de aplicación nos queda un par de detalles que configurar, pero el trabajo de codificación ya está completo. Te dejamos aquí un código de un componente raíz de aplicación en el que hemos incorporado la carga perezosa.

<link rel="import" href="../bower_components/polymer/polymer.html">

<link rel="import" href="../bower_components/iron-pages/iron-pages.html">
<link rel="import" href="../bower_components/app-route/app-location.html">
<link rel="import" href="../bower_components/app-route/app-route.html">

<dom-module id="lazy-link-app">
  <template>
    <style>
      :host {
        display: block;
      }
    </style>
    
    <app-location route="{{route}}"></app-location>
    <app-route
      route="{{route}}"
      pattern="/:page"
      data="{{data}}"
      tail="{{tail}}">
    </app-route>

    <nav>
      <a href="/productos">Productos</a>
      <a href="/mi-cuenta">Mi cuenta</a>
      <a href="/contacto">Contacto</a>
    </nav>
    
    <iron-pages
      selected="[[data.page]]"
      attr-for-selected="page"
    >
      <productos-page page="productos"></productos-page>
      <mi-cuenta-page page="mi-cuenta"></mi-cuenta-page>
      <contacto-page page="contacto"></contacto-page>
    </iron-pages>
    <p>
      [[data.page]]
    </p>
    
  </template>
  <script>
    Polymer({
      is: 'lazy-link-app',

      properties: {
        data: {
          type: Object,
        }
      },

      observers: [
        'routeChanged(data.page)'
      ],

      routeChanged: function(page) {
        console.log('routeChanged', page);
        if(page) {
          this.importHref(
            this.resolveUrl('pages/' + page + '-page.html'), null, null, true);
        }
      }
    });
  </script>
</dom-module>

Configurar el proceso de build de tu aplicación

Como puede que sepas, el Polymer CLI tiene un proceso de build en el que crea todos los archivos de tu aplicación, compactados y optimizados para una mejor descarga. Sin embargo, este proceso de build se guía por los HTML Imports que tengas para generar el archivo o archivos compactados.

Si pensamos que hemos quitado los import de varios componentes ¿cómo sabe el proceso de build qué pantallas hay en tu aplicación, con componentes que se deben cargar de manera perezosa? Dicho de otro modo ¿Cómo asegurarse que el proceso build genera los bundles de cada sección cargada con lazy load?

Esto se consigue con el archivo polymer.json. Este archivo lo tienes pre-configurado en la raíz de tu aplicación creada con el Polymer Starter Kit. Si iniciaste un proyecto desde cero, te conviene igualmente fijarte en lo que te ofrece el Polymer Starter Kit para generar tu propio polymer.json y guiar al CLI en el sistema de build.

El Polymer CLI tomará como base o componente raíz lo que tú indiques en la propiedad "shell":

"shell": "src/my-app.html",

Pero claro, al analizar ese componente raíz, el sistema de build no encontrará los imports implementados por lazy load, por lo que no colocará en el bundle principal todo ese código de esos componentes. Así pues, además del shell, para todas las rutas de tu aplicación con componentes que cargues de manera perezosa, tienes que indicarle su referencia en el array "fragments". Te quedará algo como esto:

"fragments": [
  "src/pages/productos-page.html",
  "src/pages/mi-cuenta-page.html",
  "src/pages/contacto-page.html"
],
Nota: Obviamente, las rutas que coloques aquí dependen de cómo hayas estructurado los archivos de cada componente, dentro de la carpeta "src" de tu app.

De momento es todo! hemos tenido que configurar varias cosas para que nuestra aplicación funcione con carga perezosa, pero en código has visto que es prácticamente nada.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir