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
De base,
AngularJS no permite
el registro de artefactos
después de arrancar la aplicación.
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
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:
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.
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:
- 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.