Late registering
&
Lazy loading

Gonzalo Ruiz de Villa Suárez
@gruizdevilla

Proyecto de ejemplo

Los fuentes los puedes encontrar en:

https://github.com/gonzaloruizdevilla/angularlazyload

Para arrancar el proyecto:

    $ npm install
    $ bower install
    $ npm start
    

y entra en http://localhost:3000/


De base,
AngularJS no permite
 el registro de artefactos
después de arrancar la aplicación.

http://localhost:3000/fail-late-register



Sin embargo,
se pueden acceder
a las piezas de registro
al arrancar la aplicación.



Al exponerlas, de forma artificial, podemos utilizarlas más adelante para registrar más artefactos.

Exponiendo métodos para registro posterior

http://localhost:3000/late-register

module.config(
    function (
        $controllerProvider, 
        $compileProvider,
        $filterProvider, 
        $provide
    ) {
        module.lazy = {
            controller: $controllerProvider.register,
            directive: $compileProvider.directive,
            filter: $filterProvider.register,
            factory: $provide.factory,
            service: $provide.service
        };
   }
);

Para registrar un nuevo servicio una vez arrancada la aplicación:



module.lazy.factory('MySrv', function () {
    //factory body
});

Los tests unitarios se pueden complicar. Para evitarlo:



(module.lazy || module).factory('MySrv', function () {
    //factory body
});
    

Al probar unitariamente se registran antes de arrancar.

O mejor, si hacemos esto:

http://localhost:3000/late-register-2
module.config(
    function (
        $controllerProvider,
        $compileProvider,
        $filterProvider,
        $provide
    ) {
        module.controller = $controllerProvider.register;
        module.directive = $compileProvider.directive;
        module.filter = $filterProvider.register;
        module.factory = $provide.factory;
        module.service = $provide.service;
    }
);
En la etapa de configuración, 
reemplazamos métodos inútiles por los que nos permiten registrar.

Limitaciones


  • No hay soporte oficial para estas técnicas.
  • La fase de configuración ya terminó, por lo tanto:
    • No se pueden configurar los nuevos recursos. Por lo tanto no tiene sentido registrar con provider, por ejemplo.
    • No se pueden decorar los nuevos recursos
    • No se pueden agregar nuevos módulos

En mi experiencia, estas limitaciones no son importantes para los recursos que tengo que cargar a posteriori.

Lazy load


Es importante encontrar la convención adecuada para tu proyecto: minimiza la configuración de lazy load.

La estrategia: apoyarse en las configuraciones de las rutas. Agrega en 'resolve' una función que devuelve una promesa que se resuelva al cargar las dependencias.

http://localhost:3000/lazy-load

Script.js


Para el ejemplo, uso script.js, una superligera (1.5kb) librería de carga asíncrona de scripts:


bower install script.js --save


https://github.com/ded/script.js

Preparar la configuración de una ruta:




function resolveDependencies(config, dependencies) {
  config.resolve = {
    dependencies: function ($q, $rootScope) {
      var deferred = $q.defer();
      $script(dependencies, function () {
          $rootScope.$apply(function () {
              deferred.resolve();
          });
      });
      return deferred.promise;
    }
  };
}

Usando RequireJS


Los principios son los mismos,
pero ligeramente más complicado
por la idiosincrasia de AMD:
http://localhost:3000/require

  • Pasa la referencia al módulo de AngularJS
    mediante los mecanismos de RequireJS
  • El arranque de la aplicación (bootstrap)
    de AngularJS se debe hacer de forma manual.

RequireJS o no RequireJS


  • RequireJS es mucho más potente,
    pero añade complejidad en:
    • Configuración: dependencias definidas dos veces
      en cada fichero, en 'define' y al registrar el recurso
      en AngularJS. Sin RequireJS, también hay
      esa duplicidad, pero se puede eliminar con
      convenciones en nomenclatura.
    • Testing más complicado con RequireJS.
      Con la primera técnica librería, el testing
      es el habitual de AngularJS.





¡Gracias!